Java案例怎么统计接口耗时?

wen java案例 67

本文目录导读:

Java案例怎么统计接口耗时?

  1. 基础方案:手动计算时间(最直接)
  2. AOP 方案:统一拦截(推荐)
  3. Spring Boot Actuator + Micrometer(生产环境推荐)
  4. 使用 Spring Cloud Sleuth(分布式追踪)
  5. 过滤器/拦截器方案(Web 层)
  6. 高级方案:慢查询日志与告警
  7. 总结与最佳实践

在Java开发中,统计接口耗时(API响应时间)是一个非常常见的需求,主要用于性能监控、问题排查和优化,以下是几种从简单到复杂的实现方案:

基础方案:手动计算时间(最直接)

1 使用 System.currentTimeMillis()

@GetMapping("/example")
public String example() {
    long startTime = System.currentTimeMillis();
    // 业务逻辑
    // ...
    long endTime = System.currentTimeMillis();
    long duration = endTime - startTime;
    System.out.println("接口耗时: " + duration + " ms");
    return "success";
}

2 使用 StopWatch(Spring 提供)

import org.springframework.util.StopWatch;
@GetMapping("/stopwatch")
public String stopwatchExample() {
    StopWatch stopWatch = new StopWatch("接口耗时统计");
    stopWatch.start();
    // 业务逻辑
    // ...
    stopWatch.stop();
    log.info("接口耗时: {} ms", stopWatch.getTotalTimeMillis());
    // 可以统计多个步骤
    System.out.println(stopWatch.prettyPrint());
    return "success";
}

AOP 方案:统一拦截(推荐)

使用 Spring AOP 或自定义注解,统一统计耗时,避免代码侵入。

1 创建自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TimeLog {
    String value() default "";
}

2 编写 AOP 切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimeLogAspect {
    private static final Logger log = LoggerFactory.getLogger(TimeLogAspect.class);
    @Around("@annotation(timeLog)")
    public Object around(ProceedingJoinPoint joinPoint, TimeLog timeLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - startTime;
            log.info("接口: {} 耗时: {} ms",
                    joinPoint.getSignature().toShortString(),
                    duration);
            return result;
        } catch (Throwable throwable) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("接口: {} 执行异常,耗时: {} ms",
                    joinPoint.getSignature().toShortString(),
                    duration);
            throw throwable;
        }
    }
}

3 使用注解

@RestController
public class DemoController {
    @TimeLog("查询用户信息")
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable String id) {
        // 业务逻辑
        return userService.findById(id);
    }
}

4 更通用的切面(拦截所有 Controller)

@Aspect
@Component
public class ControllerTimeAspect {
    @Around("execution(* com.example.controller.*.*(..))")
    public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        // 获取请求路径(如果是 Spring MVC)
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
            String uri = request.getRequestURI();
            String method = request.getMethod();
            log.info("[{}] {} 耗时: {} ms", method, uri, duration);
        }
        return result;
    }
}

Spring Boot Actuator + Micrometer(生产环境推荐)

用于生产环境的监控,配合 Prometheus + Grafana。

1 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

2 配置

management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus
  metrics:
    tags:
      application: your-appname

3 自定义 Metrics(可选)

import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
@Component
public class ApiMetricsCollector {
    private final MeterRegistry meterRegistry;
    public ApiMetricsCollector(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    public void recordTime(String apiName, long durationMs) {
        meterRegistry.timer("api.response.time", "api", apiName)
                .record(durationMs, TimeUnit.MILLISECONDS);
    }
}

使用 Spring Cloud Sleuth(分布式追踪)

用于微服务场景,可以查看一次请求经过的所有服务的耗时。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

配置后,日志会自动包含 Trace ID 和 Span ID,配合 Zipkin 可以可视化查看调用链路。

过滤器/拦截器方案(Web 层)

1 实现 HandlerInterceptor

@Component
public class TimeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        request.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) {
        Long startTime = (Long) request.getAttribute("startTime");
        if (startTime != null) {
            long duration = System.currentTimeMillis() - startTime;
            log.info("[{}] {} 耗时: {} ms", 
                    request.getMethod(), 
                    request.getRequestURI(), 
                    duration);
        }
    }
}

2 注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private TimeInterceptor timeInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor)
                .addPathPatterns("/**"); 
    }
}

高级方案:慢查询日志与告警

结合 AOP 和告警逻辑,超过阈值时发送告警。

@Around("@annotation(timeLog)")
public Object around(ProceedingJoinPoint joinPoint, TimeLog timeLog) throws Throwable {
    long start = System.currentTimeMillis();
    Object result = joinPoint.proceed();
    long duration = System.currentTimeMillis() - start;
    // 慢查询告警
    if (duration > 1000) { // 超过1秒
        log.warn("慢接口警告: {} 耗时 {} ms", 
                joinPoint.getSignature().toShortString(), 
                duration);
        // 发送告警邮件/钉钉通知等
        alertService.sendSlowApiAlert(joinPoint.getSignature().toShortString(), duration);
    }
    return result;
}

总结与最佳实践

方案 适用场景 优点 缺点
手动计算 临时调试 简单直接 代码侵入强
AOP 注解 日常开发 无侵入,统一管理 需要引入 AOP
Actuator + Micrometer 生产监控 可视化,与监控系统集成 部署复杂
过滤器/拦截器 Web 层统一 无需修改业务代码 无法统计 Service 层
Sleuth 微服务 全链路追踪 依赖 Zipkin

推荐方案:

  • 开发测试:使用 AOP + 自定义注解
  • 生产环境:Actuator + Micrometer + Prometheus + Grafana
  • 微服务:Sleuth + Zipkin

选择哪种方案取决于你的项目复杂度和监控需求,对于大多数中小项目,AOP 方案是最实用且侵入性最低的选择。

抱歉,评论功能暂时关闭!