Spring框架是廣泛使用的Java開發框架之一,它提供了強大的功能和靈活性,但在大型應用中,由於Spring框架的複雜性和依賴關係,應用的啟動時間和效能可能會受到影響。這可能導致開發過程中的遲緩和開發效率低下。優化Spring應用程式的啟動速度和效能是一個重要的任務,通過分析和優化應用的初始化過程、減少不必要的依賴和元件載入、並利用非同步初始化、懶載入等技術,可以顯著改善應用的啟動效能。這將幫助開發者提高開發效率、減少偵錯時間,並提供更好的使用者體驗。
線上的業務 jar 包基本上普遍比較龐大,動不動一個 jar 包幾百 M,啟動時間在10分鐘級,拖慢了我們在故障時快速擴容的響應、以及本地開發偵錯效率。於是做了一些分析,看看 Spring 程式啟動慢到底慢在哪裡,如何去優化,目前的效果是大部分大型應用啟動時間可以縮短 70%~80%。
◦ 開發效率提高:較快的應用啟動速度可以顯著減少開發和偵錯的時間。開發人員能夠更快地啟動應用程式,進行功能測試和偵錯,從而提高開發效率和迭代速度。
◦ 部署和擴充套件效率提升:優化啟動速度可以減少部署和擴充套件應用程式的時間和成本。快速啟動的應用程式可以更快地響應負載變化,提高系統的可伸縮性和彈性。
◦ 資源利用率優化:通過減少初始化時間和優化資源載入,可以降低應用程式的記憶體和CPU佔用率。這有助於提高伺服器的利用率,並降低執行應用程式的成本。
藉助Spring startup analyzer的能力,我們以業務線的ARK專案為例,深入研究如何優化提效Spring專案的啟動過程。下面我們先觀察下ARK的基本啟動情況:
這個觀察項可以一直下探,直到Bean參照的最末級,可以看出每一級的載入時長
y 軸表示呼叫棧,每一層都是一個函數。呼叫棧越深,火焰就越高,頂部就是正在執行的函數,下方都是它的父函數。
x 軸表示抽樣數,如果一個函數在 x 軸佔據的寬度越寬,就表示它被抽到的次數多,即執行的時間長。注意,x 軸不代表時間,而是所有的呼叫棧合併後,按字母順序排列的。
火焰圖就是看頂層的哪個函數佔據的寬度最大。只要有"平頂"(plateaus),就表示該函數可能存在效能問題。
顏色沒有特殊含義,因為火焰圖表示的是 CPU 的繁忙程度,所以一般選擇暖色調
從總覽圖中可以看出,有三個入口函數佔用百分比較大,下面分別看一下
這部分火焰圖可以看出,springfox在啟動過程做了很多初始化,佔了大量時間,對於不需要該功能的專案,可以直接下掉
瞭解下spring bean 的初始化過程
從這個圖中可以看出,bean的建立過程也佔了很多時間
從這個圖中可以看出,註冊BeanPostProcessor也耗費了大量時間
這一個觀察項可以蒐集到專案啟動完之後,沒有用到的Jar包
手動安裝
點選realease下載最新版tar.gz包
新建資料夾,並解壓
linux/mac系統可以考慮使用以下命令:
mkdir -p ${HOME}/spring-startup-analyzercd 下載路徑
tar -zxvf spring-startup-analyzer.tar.gz -C 安裝路徑/spring-startup-analyzer
指令碼安裝(linux/mac)
curl -sS https://raw.githubusercontent.com/linyimin0812/spring-startup-analyzer/main/bin/install.sh | sh
指令碼預設安裝路徑:$HOME/spring-startup-analyzer
spring-startup-analyzer是以agent的方式啟動的,所以在啟動命令中新增引數-javaagent:安裝路徑/spring-startup-analyzer/lib/spring-profiler-agent.jar
即可。
java -javaagent:/Users/runner/spring-startup-analyzer/lib/spring-profiler-agent.jar \
-Dproject.name=mac-demo \
-Dspring-startup-analyzer.admin.http.server.port=8066 \
-jar /Users/runner/spring-startup-analyzer/ARK.jar
紀錄檔檔案路徑:安裝路徑/spring-startup-analyzer/logs
應用啟動完成後會在console和startup.log檔案中輸出======= spring-startup-analyzer finished, click http://localhost:xxxx to visit details. ======
,可以通過此輸出來判斷採集是否完成。
預發平均啟動10分鐘,本地無法啟動,每次需求需要提交到預發環境驗證,開發和發版週期比較長,且預發環境連線的生產庫,不能隨便造數。專案參照585個jar,其中有337個jar沒用到。
分析可以看到,耗時排名前面的介面都是jsf相關的載入,還有一個es相關的bean。
功能路徑:Details of Method Invoke --> AbstractAutowireCapableBeanFactory.createBean
注:index=「註冊中心地址」中的「註冊中心地址「做了匿名,在具體場景檢視自己程式碼中的設定
jsf的生產者的註冊中心在啟動的時候,會拉取一批ip,不斷嘗試註冊jsf,在辦公環境這些ip無法存取,導致啟動過程一直重試
<!-- 預發、生產的註冊中心 -->
<jsf:registry id="jsfRegistry" protocol="jsfRegistry" index="註冊中心地址"/>
在本機host裡面增加jsf釋出地址的host設定,下面*...* 在使用的時候替換成自己的,可以 ping test.註冊中心地址 獲取。「註冊中心地址」 替換成上面index後面的地址
*.*.*.* 註冊中心地址
再次啟動專案,時長來到185s
將ES的Bean初始化進行懶載入,以及開啟全域性懶載入,時長來到131s;
全域性懶載入:
1、根據spring版本的不同,開啟全域性懶載入的方式可能會不相同
2、不建議生產環境開啟全域性懶載入,因為基本上我們的服務都是部署在k8s上的,有可能服務在伸縮的時候,在存取量大的時候,由於懶載入的設定,服務快速啟動成功了,會返回給docker容器服務已經準備就緒狀態,導致k8s把流量分給該服務,導致預想不到的問題。
對於應用未使用的jar包,可以謹慎剔除,在剔除的時候一個一個下,每下一個都要重複編譯和啟動驗證是否會對專案造成影響,這是一個持續和長期的過程,Jar瘦身不僅對啟動時長有收益,而且對編譯提效很明顯,減少了大量的Jar複製過程
做完上述優化之後:
https://www.oracle.com/java/technologies/downloads/
請聯絡作者提供免費賬號
https://redis.io/docs/install/install-redis/install-redis-on-windows/
https://github.com/linyimin0812/spring-startup-analyzer/blob/main/README_ZH.md
作者:京東健康 樑燦
來源:京東雲開發者社群 轉載請註明來源