欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot監(jiān)控API請求耗時的6中解決解決方案

 更新時間:2025年07月28日 11:20:11   作者:星辰聊技術(shù)  
本文介紹SpringBoot中記錄API請求耗時的6種方案,包括手動埋點、AOP切面、攔截器、Filter、事件監(jiān)聽、Micrometer+Prometheus,具有一定的參考價值,感興趣的可以了解一下

1. 簡介

在微服務(wù)架構(gòu)與高并發(fā)場景下,API接口的響應(yīng)速度直接影響用戶體驗與系統(tǒng)穩(wěn)定性。隨著業(yè)務(wù)復(fù)雜度提升,接口性能問題逐漸成為系統(tǒng)瓶頸,例如數(shù)據(jù)庫查詢延遲、第三方服務(wù)調(diào)用超時等場景,均可能導(dǎo)致接口耗時激增。傳統(tǒng)的手動埋點統(tǒng)計方式(如在每個接口方法中記錄開始與結(jié)束時間)存在代碼侵入性強、維護成本高的問題,難以滿足大規(guī)模接口的監(jiān)控需求。

本篇文章將介紹 Spring Boot 中記錄 API 請求耗時的 6 種實用方案,涵蓋從基礎(chǔ)到進階的多種實現(xiàn)方式,幫助開發(fā)者根據(jù)業(yè)務(wù)場景選擇最適合的監(jiān)控手段。

2.實戰(zhàn)案例

2.1 手動記錄

我們可以通過Spring內(nèi)置的StopWatch工具進行記錄方法執(zhí)行耗時情況:

@GetMapping("/query")
public ResponseEntity<?> query() throws Exception {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  // 業(yè)務(wù)邏輯
  TimeUnit.MILLISECONDS.sleep(new Random().nextLong(2000)) ;
  stopWatch.stop();
  System.out.printf("方法耗時:%dms%n", stopWatch.getTotalTimeMillis()) ;
  return ResponseEntity.ok("api query...") ;
}

運行結(jié)果

方法耗時:1096ms

針對手動記錄這里總結(jié)2點缺點:

  • 代碼侵入性強:每次需要記錄請求耗時的時候都需要在具體的業(yè)務(wù)邏輯中插入相應(yīng)的計時代碼(如使用System.currentTimeMillis()或StopWatch)。這種方式會增加代碼的復(fù)雜度,并且使得業(yè)務(wù)邏輯與性能監(jiān)控代碼耦合在一起,降低了代碼的可讀性和維護性。
  • 重復(fù)工作:如果多個地方都需要進行類似的耗時統(tǒng)計,則可能需要在每個地方都添加相同的代碼,這導(dǎo)致了代碼的重復(fù)。違反了DRY(Don't Repeat Yourself)原則。

2.2 自定義AOP記錄

通過自定義注解結(jié)合Spring AOP切面編程,可實現(xiàn)無侵入式的方法執(zhí)行耗時統(tǒng)計與記錄。

@Aspect
@Component
public class PerformanceAspect {


  private static final Logger logger = LoggerFactory.getLogger("api.timed") ;
  @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping) || "
      + "@annotation(org.springframework.web.bind.annotation.GetMapping) || "
      + "@annotation(org.springframework.web.bind.annotation.PostMapping) || "
      + "@annotation(org.springframework.web.bind.annotation.PutMapping) || "
      + "@annotation(org.springframework.web.bind.annotation.DeleteMapping) || "
      + "@annotation(org.springframework.web.bind.annotation.PatchMapping)")
  public Object recordExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
    StopWatch sw = new StopWatch();
    sw.start();
    Object result = pjp.proceed();
    sw.stop();
    logger.info("方法【{}】耗時: {}ms", pjp.getSignature(), sw.getTotalTimeMillis()) ;
    return result;
  }
}

在該示例,我們并沒有自定義注解,而是直接攔截了定義Controller接口使用的注解。

運行結(jié)果

Line:29 - 方法【ResponseEntity ApiController.query()】耗時: 487ms

總結(jié):AOP實現(xiàn)耗時記錄非侵入、統(tǒng)一管理、減少重復(fù)代碼,適合全局監(jiān)控。對非Spring管理的方法無效,增加切面復(fù)雜度,可能影響性能。

2.3 攔截器技術(shù)

通過攔截器不用修改任何業(yè)務(wù)代碼就能非常方便的記錄方法執(zhí)行耗時情況。

public class TimedInterceptor 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");
    long cost = System.currentTimeMillis() - startTime;
    System.out.printf("請求【%s】耗時: %dms%n", request.getRequestURI(), cost) ;
  }
}

注冊攔截器

@Component
public class InterceptorConfig implements WebMvcConfigurer {
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new TimedInterceptor())
      .addPathPatterns("/api/**") ;
  }
}

運行結(jié)果

請求【/api/query】耗時: 47ms

總結(jié):攔截器可集中管理請求耗時記錄,減少代碼侵入性,適用于Controller層統(tǒng)一監(jiān)控。僅對Web請求(Controller)生效,無法捕獲非HTTP接口或內(nèi)部方法調(diào)用耗時,粒度較粗。

2.4 使用Filter

Filter 是 Servlet 規(guī)范中的過濾器,用于在請求到達目標(biāo)資源前后進行攔截處理,可用于日志記錄、權(quán)限校驗等通用邏輯。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE) // 確保最先執(zhí)行
public class RequestTimingFilter implements Filter {
  private static final PathPatternParser parser = new PathPatternParser();


  private static final Logger logger = LoggerFactory.getLogger(RequestTimingFilter.class);
  // 從配置文件中讀取排除路徑
  @Value("${timing.filter.exclude-paths}")
  private String[] excludePaths;
  // 路徑匹配器緩存
  private List<PathPattern> excludedPatterns = Collections.emptyList();
  @Override
  public void init(FilterConfig filterConfig) {
    // 初始化時編譯排除路徑的正則表達式
    excludedPatterns = Arrays.stream(excludePaths).map(path -> parser.parse(path)).toList() ;
    logger.info("記錄請求耗時,不記錄的URI: {}", Arrays.toString(excludePaths));
  }
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String requestURI = httpRequest.getRequestURI();
    // 檢查是否在排除路徑中
    if (shouldExclude(requestURI)) {
      chain.doFilter(request, response);
      return;
    }
    long startTime = System.nanoTime();
    try {
      // 執(zhí)行后續(xù)過濾器鏈和實際請求處理
      chain.doFilter(request, response);
    } finally {
      long endTime = System.nanoTime();
      long durationNanos = endTime - startTime;
      long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
      // 記錄日志(包含請求方法和狀態(tài)碼)
      if (response instanceof HttpServletResponse httpResponse) {
        int status = httpResponse.getStatus();
        logger.info("[{}] {} - {}ms (Status: {})", httpRequest.getMethod(), requestURI, durationMillis, status);
      } else {
        logger.info("[{}] {} - {}ms", httpRequest.getMethod(), requestURI, durationMillis);
      }
    }
  }
  private boolean shouldExclude(String requestURI) {
    return excludedPatterns.stream()
        .anyMatch(pattern -> pattern.matches(PathContainer.parsePath(requestURI))) ;
  }
}

運行結(jié)果

RequestTimingFilter Line:77  - [GET] /api/query - 379ms (Status: 200)

總結(jié):Filter 實現(xiàn)耗時記錄非侵入,適用于全局請求監(jiān)控,配置簡單;僅能記錄整個請求的處理時間,無法精確到具體方法或業(yè)務(wù)邏輯,粒度較粗。

2.5 通過事件監(jiān)聽

在Spring MVC底層內(nèi)部,當(dāng)一個請求處理完成以后會發(fā)布ServletRequestHandledEvent 事件,通過監(jiān)聽該事件就能獲取請求的詳細信息。

@Component
public class TimedListener {
  @EventListener(ServletRequestHandledEvent.class)
  public void recordTimed(ServletRequestHandledEvent event) {
    System.err.println(event) ;
  }
}

運行結(jié)果

ServletRequestHandledEvent: url=[/api/query]; 
  client=[0:0:0:0:0:0:0:1]; method=[GET]; status=[200]; 
  servlet=[dispatcherServlet]; session=[null]; user=[null]; 
  time=[696ms]

詳細的輸出了當(dāng)前請求的信息。

總結(jié):非侵入式獲取請求處理耗時,適用于全局監(jiān)控且無需修改業(yè)務(wù)代碼;僅能獲取整個請求的耗時信息,無法定位具體方法或模塊性能問題,粒度較粗。

2.6 Micrometer + Prometheus

Micrometer 是一個指標(biāo)度量工具,支持多種監(jiān)控系統(tǒng),如 Prometheus。通過 @Timed 注解可輕松記錄方法耗時;Prometheus 是開源監(jiān)控系統(tǒng),擅長拉取和聚合指標(biāo),常與 Micrometer 集成實現(xiàn)可視化監(jiān)控。兩者結(jié)合適合微服務(wù)性能觀測。

@Timed(value = "api.query", description = "查詢業(yè)務(wù)接口")
@GetMapping("/query")
public ResponseEntity<?> query() throws Exception {
  TimeUnit.MILLISECONDS.sleep(new Random().nextLong(2000)) ;
  return ResponseEntity.ok("api query...") ;
}

在需要監(jiān)控的接口上添加 @Timed 注解,同時你還需要進行如下的配置:

management:
  observations:
    annotations:
      enabled: true

要結(jié)合Prometheus,那么我們還需要引入如下依賴

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

配置actuator暴露Prometheus端點

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

最后,我們還需要在Prometheus中進行配置

- job_name: "testtag"
  metrics_path: "/ac/prometheus"
  static_configs:
    - targets: ["localhost:8080"]

總結(jié):非侵入、集成簡單,支持細粒度方法級監(jiān)控,與Spring Boot天然兼容,數(shù)據(jù)可持久化并可視化;需引入監(jiān)控組件,增加系統(tǒng)復(fù)雜度。

2.7 使用Arthas

Arthas 是一款線上監(jiān)控診斷產(chǎn)品,通過全局視角實時查看應(yīng)用 load、內(nèi)存、gc、線程的狀態(tài)信息,并能在不修改應(yīng)用代碼的情況下,對業(yè)務(wù)問題進行診斷,包括查看方法調(diào)用的出入?yún)?、異常,監(jiān)測方法執(zhí)行耗時,類加載信息等,大大提升線上問題排查效率。

首先,在如下地址下載最新的Arthas

https://github.com/alibaba/arthas/releases

接下來,通過如下命令啟動Arthas

java -jar arthas-boot.jar

通過前面的數(shù)字選擇哪個進程。

連接到具體的進程后,我們可以通過如下的命令來跟蹤記錄方法執(zhí)行耗時情況

trace com.pack.timed.controller.ApiController query

2.8 使用SkyWalking

SkyWalking 是一個開源的 APM(應(yīng)用性能監(jiān)控)系統(tǒng),支持分布式鏈路追蹤、服務(wù)網(wǎng)格觀測、度量聚合與可視化,適用于微服務(wù)、云原生和 Service Mesh 架構(gòu),幫助開發(fā)者實現(xiàn)全棧性能監(jiān)控與故障診斷。

我們無需在代碼中寫任何代碼或是引入依賴,因為SkyWalking將使用agent技術(shù)進行跟蹤系統(tǒng)。

首先,我們需要在如下地址下載SkyWalking已經(jīng)java對應(yīng)的agent。

skywalking.apache.org/downloads/

Web UI 運行在8080端口

最后,我們在運行程序時,需要加入如下JVM參數(shù)。

-javaagent:D:\all\opensource\skywalking\skywalking-agent\skywalking-agent.jar 
-Dskywalking.agent.service_name=pack-api
1.2.

總結(jié):SkyWalking 自動完成鏈路追蹤與耗時監(jiān)控,支持分布式系統(tǒng),無需修改代碼,可視化強,性能影響小。在分布式系統(tǒng)中非常推薦。

到此這篇關(guān)于SpringBoot監(jiān)控API請求耗時的6中解決解決方案的文章就介紹到這了,更多相關(guān)SpringBoot監(jiān)控API請求耗時內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java程序啟動時初始化數(shù)據(jù)的四種方式

    Java程序啟動時初始化數(shù)據(jù)的四種方式

    本文主要介紹了Java程序啟動時初始化數(shù)據(jù)的四種方式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • java實現(xiàn)mongodb的數(shù)據(jù)庫連接池

    java實現(xiàn)mongodb的數(shù)據(jù)庫連接池

    這篇文章主要介紹了基于java實現(xiàn)mongodb的數(shù)據(jù)庫連接池,Java通過使用mongo-2.7.3.jar包實現(xiàn)mongodb連接池,感興趣的小伙伴們可以參考一下
    2015-12-12
  • IDEA代碼規(guī)范&質(zhì)量檢查的實現(xiàn)

    IDEA代碼規(guī)范&質(zhì)量檢查的實現(xiàn)

    這篇文章主要介紹了IDEA代碼規(guī)范&質(zhì)量檢查的實現(xiàn),文中通過圖文介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 使用JVMTI實現(xiàn)SpringBoot的jar加密,防止反編譯

    使用JVMTI實現(xiàn)SpringBoot的jar加密,防止反編譯

    這篇文章主要介紹了使用JVMTI實現(xiàn)SpringBoot的jar加密,防止反編譯問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • 關(guān)于mybatis-plus-generator的簡單使用示例詳解

    關(guān)于mybatis-plus-generator的簡單使用示例詳解

    在springboot項目中集成mybatis-plus是很方便開發(fā)的,最近看了一下plus的文檔,簡單用一下它的代碼生成器,接下來通過實例代碼講解關(guān)于mybatis-plus-generator的簡單使用,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • 詳細了解java監(jiān)聽器和過濾器

    詳細了解java監(jiān)聽器和過濾器

    下面小編就為大家?guī)硪黄趈ava servlet過濾器和監(jiān)聽器(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-07-07
  • 詳細解讀Java編程中面向字符的輸入流

    詳細解讀Java編程中面向字符的輸入流

    這篇文章主要介紹了Java中面向字符的輸入和輸出流,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-10-10
  • 使用Java校驗SQL語句的合法性五種解決方案

    使用Java校驗SQL語句的合法性五種解決方案

    這篇文章主要介紹了如何用java校驗SQL語句的合法性(提供五種解決方案),使用JDBC?API和JSqlParser庫、正則表達式、ANTLR解析器生成器或Apache?Calcite庫都可以實現(xiàn)校驗SQL語句的合法性,需要的朋友可以參考下
    2023-04-04
  • 使用Java打印數(shù)字組成的魔方陣及字符組成的鉆石圖形

    使用Java打印數(shù)字組成的魔方陣及字符組成的鉆石圖形

    這篇文章主要介紹了使用Java打印數(shù)字組成的魔方陣及字符組成的鉆石圖形,可作為一些CLI程序界面的基礎(chǔ)部分,需要的朋友可以參考下
    2016-03-03
  • springboot項目之相互依賴報錯問題(基于idea)

    springboot項目之相互依賴報錯問題(基于idea)

    這篇文章主要介紹了springboot項目之相互依賴報錯問題(基于idea),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評論