盤(pán)點(diǎn)多種方法耗時(shí)統(tǒng)計(jì)的實(shí)操解法
前言
在應(yīng)用開(kāi)發(fā)里,精準(zhǔn)把控方法執(zhí)行耗時(shí),對(duì)優(yōu)化系統(tǒng)性能、提升用戶(hù)體驗(yàn)意義重大。下面將介紹幾種不同的方法耗時(shí)統(tǒng)計(jì)方式,每種都有其獨(dú)特的適用場(chǎng)景。
System.currentTimeMillis ()
在方法內(nèi)借助System.currentTimeMillis()記錄起始和結(jié)束時(shí)間,進(jìn)而算出方法執(zhí)行耗時(shí)。這種方式簡(jiǎn)單直接,無(wú)需引入額外依賴(lài)。
@Service
public class BookService {
public Book getBookById(Long id) {
long startTime = System.currentTimeMillis();
// 模擬從數(shù)據(jù)庫(kù)獲取書(shū)籍信息的業(yè)務(wù)邏輯
Book book = bookRepository.findById(id).orElse(null);
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
System.out.println("getBookById方法耗時(shí):" + elapsedTime + "ms");
return book;
}
}
利用 StopWatch
@Service
public class UserService {
public User findUserById(Long id) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 模擬業(yè)務(wù)邏輯,這里從數(shù)據(jù)庫(kù)查詢(xún)用戶(hù)信息
User user = userRepository.findById(id).orElse(null);
stopWatch.stop();
System.out.println("findUserById方法執(zhí)行耗時(shí):" + stopWatch.getTotalTimeMillis() + "毫秒");
return user;
}
}
使用AOP實(shí)現(xiàn)全局方法
在 項(xiàng)目中配置aop依賴(lài)后,就能利用它進(jìn)行方法耗時(shí)統(tǒng)計(jì)
@Aspect
@Component
public class MethodTimeAspect {
@Around("execution(* com.example.demo.service.*.*(..))")
public Object measureTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
String methodName = joinPoint.getSignature().getName();
System.out.println("方法[" + methodName + "]耗時(shí):" + elapsedTime + "ms");
return result;
}
}
利用 HandlerInterceptor 統(tǒng)計(jì)
Spring的HandlerInterceptor可用于監(jiān)控Controller層接口的耗時(shí)情況
public class ControllerTimeInterceptor implements HandlerInterceptor {
private ThreadLocal<Long> startTime = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
startTime.set(System.currentTimeMillis());
returntrue;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime.get();
String uri = request.getRequestURI();
System.out.println("接口[" + uri + "]耗時(shí):" + elapsedTime + "ms");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 可在此處進(jìn)行資源清理等操作
startTime.remove();
}
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ControllerTimeInterceptor())
.addPathPatterns("/api/**");
}
}
基于 Servlet Filter 統(tǒng)計(jì)
通過(guò)創(chuàng)建Servlet Filter,可記錄HTTP請(qǐng)求的整體耗時(shí)
@WebFilter("/*")
public class RequestTimeFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
String requestUri = ((HttpServletRequest) request).getRequestURI();
System.out.println("請(qǐng)求[" + requestUri + "]耗時(shí):" + elapsedTime + "ms");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
使用ServletRequestHandledEvent統(tǒng)計(jì)
在Spring框架中,監(jiān)控接口請(qǐng)求的性能可以通過(guò)ServletRequestHandledEvent事件實(shí)現(xiàn)
@Component
public class TimeCountListener implements ApplicationListener<ServletRequestHandledEvent> {
@Override
public void onApplicationEvent(ServletRequestHandledEvent event) {
Throwable failureCause = event.getFailureCause();
if (failureCause != null) {
System.err.printf("錯(cuò)誤原因: %s%n", failureCause.getMessage());
}
System.err.println("========================================");
System.err.printf("請(qǐng)求客戶(hù)端地址:%s\n請(qǐng)求URL: %s\n請(qǐng)求Method: %s\n請(qǐng)求耗時(shí): %d毫秒%n",
event.getClientAddress(),
event.getRequestUrl(),
event.getMethod(),
event.getProcessingTimeMillis());
System.err.println("========================================");
}
}
實(shí)際開(kāi)發(fā)時(shí),需根據(jù)項(xiàng)目規(guī)模、業(yè)務(wù)場(chǎng)景、性能需求等因素綜合考慮,選擇最合適的方法,從而高效地進(jìn)行性能優(yōu)化。