手写!Controller接口性能监控

本文将介绍如何在Spring Boot应用中通过Spring Boot Actuator和Prometheus来监控任意API接口的调用耗时情况。通过Actuator提供度量metrics功能,我们能够结合AOP轻松实现API接口运行时性能指标。
首页 新闻资讯 行业资讯 手写!Controller接口性能监控

1. 简介

本文将介绍如何在Spring Boot应用中通过Spring Boot Actuator和Prometheus来监控任意API接口的调用耗时情况。通过Actuator提供度量metrics功能,我们能够结合AOP轻松实现API接口运行时性能指标。结合Prometheus这一强大的监控系统,我们能够实时地查看和分析API接口的调用耗时,进而评估应用的性能状况。

2. 环境准备

2.1 依赖管理

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

引入Prometheus依赖包,将所有的指标数据导出为Prometheus格式。Prometheus会通过actuator接口拉取数据。同时还会注册一个/prometheus接口。

配置文件

management:
  endpoints:
    web:
      base-path:/ac
      exposure:
        include:'*'

暴露所有的端点。

3. 实战案例

本案例统计Controller接口的耗时情况,为了简单我们通过注解的方式标注需要统计的方法。

3.1 定义注解

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public@interfaceMonitor {// 作用是为不同的接口打标记String[]tags()default{};}

其实你还可以定义比如:指标名的属性,这样可以为不同的Controller做统计。

3.2 定义切面

该切面的作用用来拦截所有使用了@Monitor注解的Controller方法,通过环绕通知进行计时处理。

@Component@Aspectpublicclass MonitorAspect {
  
  private final MeterRegistry meterRegistry;// 度量名称(你可以通过注解自定义)private static final String API_TIMER_METER_NAME="myapp.api.timer";publicMonitorAspect(MeterRegistry meterRegistry){
    this.meterRegistry=meterRegistry;}@Pointcut("@annotation(monitor)")private void pcMonitor(Monitor monitor){};@Around("pcMonitor(monitor)")publicObject around(ProceedingJoinPoint pjp,Monitor monitor)throws Throwable {
    Timer.Sample sample=Timer.start(this.meterRegistry);String[]tags=monitor.tags();Object ret=null;Throwable ex=null;try {
      ret=pjp.proceed();} catch(Throwable th){
      ex=th;throw th;} finally {
      List<String>listTags=new ArrayList<>();listTags.addAll(Arrays.asList(tags));// 出现异常也会将异常名称打入tagif(Objects.nonNull(ex)){
        listTags.add(ex.getClass().getSimpleName());}
      Timer timer=meterRegistry.timer(API_TIMER_METER_NAME,listTags.toArray(new String[0]));sample.stop(timer);}returnret;}
}

以上切面非常的简单,统计方法执行的耗时情况。

3.3 定义接口

@Servicepublicclass UserService {
  
  private static final List<User>DATAS=List.of(newUser(1L,"张三","男",22),newUser(2L,"李四","男",23),newUser(3L,"王五","女",22),newUser(4L,"赵六","男",32));publicList<User>queryUsers(){
    sleep(2000);returnDATAS;}publicUserqueryById(Long id){
    sleep(1000);returnDATAS.stream().filter(user->user.getId()==id).findFirst().orElse(null);}
  
  private void sleep(inttime){// 模拟耗时try {
      TimeUnit.MILLISECONDS.sleep(new Random().nextInt(time));} catch(InterruptedException e){}
  }
}

Controller接口

@Monitor(tags={"UserController","list"})@GetMapping("")publicList<User>list(){returnthis.userService.queryUsers();}@Monitor(tags={"UserController","ById"})@GetMapping("/{id}")publicUserqueryById(@PathVariableLong id){returnthis.userService.queryById(id);}

注意:这里的注解属性tags必须是偶数,因为内部会通过提供的字符串tag组装成Tag对象,而Tag对象需要Key/Value。

以上是所需的所有代码,接下来进行测试。

分别访问上面的两个接口。

图片图片

图片图片

分别多次访问上面的接口,通过/ac/metrics/myapp.api.timer查看指标信息。

图片图片

通过Prometheus查看图形走势。

图片图片


通过图表方式,查看到每个接口在不同时刻的请求耗时情况。

33    2024-08-12 12:20:49    Controller 接口 性能