最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot首筆交易慢問題排查與優(yōu)化方案

 更新時間:2025年04月07日 15:19:13   作者:MrWho  
在我們的微服務項目中,遇到這樣的問題:應用啟動后,第一筆交易響應耗時高達4、5秒,而后續(xù)請求均能在毫秒級完成,這不僅觸發(fā)監(jiān)控告警,也極大影響了用戶體驗,本文將結(jié)合日志排查、性能工具分析以及預熱優(yōu)化手段,總結(jié)出一套完整的排查思路和解決方案

問題背景

在我們的微服務系統(tǒng)中,首筆交易響應明顯偏慢,經(jīng)過初步排查發(fā)現(xiàn):

  • Flowable 流程部署、Redis 連接建立、PageHelper 代理生成和 Hibernate Validator 校驗等操作均集中在首筆交易時進行;
  • 后續(xù)交易響應迅速,說明業(yè)務邏輯本身并無性能瓶頸,而主要問題出在各類資源的首次初始化上。

這種“懶加載”機制雖然能夠延遲資源加載,但在首筆交易時往往會導致嚴重延時,影響整體體驗。實際項目中需平衡啟動速度與首次響應效率,主動預熱關鍵組件。

排查步驟

1. 日志分析

首先,將日志級別調(diào)為 DEBUG,詳細觀察首筆交易與后續(xù)交易之間的差異。
在 Flowable 工作流啟動時,日志中會出現(xiàn)如下部署信息:

2025-03-31-15:24:25:326 [thread1] DEBUG o.f.e.i.bpmn.deployer.BpmnDeployer.deploy.72 -- Processing deployment SpringBootAutoDeployment
2025-03-31-15:24:25:340 [thread1] DEBUG o.f.e.i.b.d.ParsedDeploymentBuilder.build.54 -- Processing BPMN resource E:\gitProjects\flowableProject\target\classes\processes\eib.bpmn20.xml

同樣,Redis 連接在首次調(diào)用時會看到大量lettuce包日志,如:

2025-03-31-15:24:23:587 [XNIO-1 task-1] DEBUG io.lettuce.core.RedisClient.initializeChannelAsync0.304 -- Connecting to Redis at 10.240.75.250:7379

這些信息表明,在首次調(diào)用時,系統(tǒng)才開始部署流程、建立 Redis 連接以及加載其它第三方組件,從而導致延遲。

2. 性能工具定位

由于單純依賴日志排查比較繁瑣,我們還使用了 Java VisualVM(JDK 自帶工具,也可選擇其它工具)進行采樣分析。
在 VisualVM 中選擇目標進程后通過 CPU 取樣,示意圖如下(也可配置JMX遠程連接)。

觀察結(jié)果如下:

發(fā)現(xiàn)首筆交易相比后續(xù)交易多出以下方法的調(diào)用(省略的部分二方包慢代碼):

  • com.github.pagehelper.dialect.auto.DataSourceAutoDialect.<init>
  • org.hibernate.validator.internal.engine.ValidatorImpl.validate()

這些方法的初始化也成為首筆交易慢的原因之一。

優(yōu)化方案:提前預熱各種資源

針對上述問題,我們的優(yōu)化思路很簡單:提前初始化各項資源,確保首筆交易時不再觸發(fā)大量懶加載。為此,我們將所有預熱操作改寫成基于 ApplicationRunner 的實現(xiàn),保證在 Spring Boot 啟動后就自動執(zhí)行。

1. Flowable 流程部署預熱

在應用啟動時,通過掃描 BPMN 文件提前部署流程,避免在交易中首次部署導致延遲。

import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.DeploymentBuilder;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

@Component
public class ProcessDeploymentRunner implements ApplicationRunner {

    private final RepositoryService repositoryService;

    public ProcessDeploymentRunner(RepositoryService repositoryService) {
        this.repositoryService = repositoryService;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 掃描 processes 目錄下的所有 BPMN 文件
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath:/processes/*.bpmn20.xml");

        if (resources.length == 0) {
            System.out.println("未在 processes 目錄下找到 BPMN 文件");
            return;
        }

        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                .name("自動部署流程");

        for (Resource resource : resources) {
            deploymentBuilder.addInputStream(resource.getFilename(), resource.getInputStream());
        }

        deploymentBuilder.deploy();
        System.out.println("流程定義已部署,數(shù)量:" + resources.length);
    }
}

2. Redis 連接預熱

利用 ApplicationRunner 發(fā)送一次 PING 請求,提前建立 Redis 連接,避免首筆交易時因連接建立而耗時。

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class RedisWarmupRunner implements ApplicationRunner {

    private final StringRedisTemplate redisTemplate;

    public RedisWarmupRunner(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void run(ApplicationArguments args) {
        try {
            String pingResult = redisTemplate.getConnectionFactory().getConnection().ping();
            System.out.println("? Redis connection pre-warmed successfully: " + pingResult);
        } catch (Exception e) {
            System.err.println("? Redis warm-up failed: " + e.getMessage());
        }
    }
}

3. PageHelper 預熱

通過執(zhí)行一條簡單的查詢語句,觸發(fā) PageHelper 及相關 MyBatis Mapper 的初始化。

import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class PageHelperWarmupRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        try {
            boolean result = SqlRunner.db().selectObjs("SELECT 1").size() > 0;
            System.out.println("? PageHelper & SqlRunner pre-warm completed, result: " + result);
        } catch (Exception e) {
            System.err.println("? PageHelper pre-warm failed: " + e.getMessage());
        }
    }
}

(請確保配置文件中已開啟 SQL Runner 功能:
mybatis-plus.global-config.enable-sql-runner=true

4. Hibernate Validator 預熱

通過一次 dummy 校驗操作,提前加載 Hibernate Validator 相關類和反射邏輯

import jakarta.validation.Validation;
import jakarta.validation.Validator;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class ValidatorWarmupRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        try {
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            DummyEntity dummy = new DummyEntity();
            validator.validate(dummy);
            System.out.println("? Hibernate Validator pre-warm completed!");
        } catch (Exception e) {
            System.err.println("? Hibernate Validator pre-warm failed: " + e.getMessage());
        }
    }

    private static class DummyEntity {
        @jakarta.validation.constraints.NotNull
        private String name;
    }
}

5. Undertow 預熱(可選)

如果使用 Undertow 作為內(nèi)嵌服務器,也可以通過主動發(fā)送 HTTP 請求預熱相關資源。此外,在配置文件中開啟過濾器提前初始化也有助于降低延遲。

在 application.yml 中設置:

server:
  undertow:
    eager-init-filters: true

再通過下面的代碼發(fā)送一次預熱請求:

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class UndertowWarmupRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        try {
            RestTemplate restTemplate = new RestTemplate();
            String response = restTemplate.getForObject("http://localhost:8080/health", String.class);
            System.out.println("? Undertow pre-warm completed, response: " + response);
        } catch (Exception e) {
            System.err.println("? Undertow pre-warm failed: " + e.getMessage());
        }
    }
}

總結(jié)

通過上述方案,我們將 Flowable 流程部署、Redis 連接、PageHelper 初始化、Hibernate Validator 校驗和 Undertow 相關組件的預熱操作全部遷移到 ApplicationRunner 中,在應用啟動后就自動執(zhí)行。這樣,首筆交易時不再需要進行大量初始化工作,各項資源已預先加載,確保后續(xù)請求能達到毫秒級響應,大大提升了用戶體驗并避免了無效的監(jiān)控告警。

以上就是SpringBoot首筆交易慢問題排查與優(yōu)化方案的詳細內(nèi)容,更多關于SpringBoot首筆交易慢問題的資料請關注腳本之家其它相關文章!

相關文章

  • 編寫Spring MVC控制器的14個技巧(小結(jié))

    編寫Spring MVC控制器的14個技巧(小結(jié))

    這篇文章主要介紹了編寫Spring MVC控制器的14個技巧,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-11-11
  • Spring聲明式事務和@Aspect的攔截順序問題的解決

    Spring聲明式事務和@Aspect的攔截順序問題的解決

    本篇文章主要介紹了Spring聲明式事務和@Aspect的攔截順序問題的解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • Java圖形界面Swing原理及用法解析

    Java圖形界面Swing原理及用法解析

    這篇文章主要介紹了Java圖形界面Swing原理及用法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 解決@CachePut設置的key值無法與@CacheValue的值匹配問題

    解決@CachePut設置的key值無法與@CacheValue的值匹配問題

    這篇文章主要介紹了解決@CachePut設置的key的值無法與@CacheValue的值匹配問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 淺談Java向下轉(zhuǎn)型的意義

    淺談Java向下轉(zhuǎn)型的意義

    這篇文章主要介紹了淺談Java向下轉(zhuǎn)型的意義,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-02-02
  • Spring多線程通過@Scheduled實現(xiàn)定時任務

    Spring多線程通過@Scheduled實現(xiàn)定時任務

    這篇文章主要介紹了Spring多線程通過@Scheduled實現(xiàn)定時任務,@Scheduled?定時任務調(diào)度注解,是spring定時任務中最重要的,下文關于其具體介紹,需要的小伙伴可以參考一下
    2022-05-05
  • SSH框架網(wǎng)上商城項目第4戰(zhàn)之EasyUI菜單的實現(xiàn)

    SSH框架網(wǎng)上商城項目第4戰(zhàn)之EasyUI菜單的實現(xiàn)

    SSH框架網(wǎng)上商城項目第4戰(zhàn)之EasyUI菜單的實現(xiàn),本文主要使用EasyUI技術簡單實現(xiàn)后臺菜單,感興趣的小伙伴們可以參考一下
    2016-05-05
  • 關于MyBaties的基本配置標簽總結(jié)

    關于MyBaties的基本配置標簽總結(jié)

    今天給大家?guī)淼氖顷P于MyBaties基礎的相關知識,文章圍繞著MyBaties的基本配置標簽展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Spring MVC文件配置以及參數(shù)傳遞示例詳解

    Spring MVC文件配置以及參數(shù)傳遞示例詳解

    這篇文章主要給大家介紹了關于Spring MVC文件配置以及參數(shù)傳遞的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • 使用Java實現(xiàn)獲取客戶端IP地址

    使用Java實現(xiàn)獲取客戶端IP地址

    這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)獲取客戶端IP地址,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-02-02

最新評論

托里县| 乌兰县| 贵港市| 增城市| 无锡市| 湄潭县| 英吉沙县| 福安市| 九龙县| 南宁市| 城口县| 名山县| 万州区| 宜宾市| 万宁市| 襄樊市| 建始县| 新巴尔虎右旗| 东山县| 鹤峰县| 汤原县| 务川| 三亚市| 甘南县| 江城| 泗水县| 邮箱| 东莞市| 阳城县| 淮阳县| 错那县| 交口县| 巩义市| 保山市| 肇源县| 邹城市| 红桥区| 青冈县| 浦城县| 江永县| 东兰县|