天天看點

springboot 啟動速度調優

作者:江滬白

目前我們的微服務啟動速度都談不上快. 在添加了預熱之後啟動速度會更加的感人. 這個原因通過研究以及行業相關的解決方案.

注: 這裡所說的方法,都是以類加載速度瓶頸優化展開的. 這個是已經在使用工具.如 async-profiler此類的工具對啟動速度進行了定位分析後的優化方向.

對于SpringBoot 本身

目前可能解決辦法有:

  • 更新 springBoot到2.x , 2.x以後 spring-boot-loader的class 加載器,對于類的索引與查找有優化.更新2.x需要先更新到最新版本的 1.5.x. 然後再更新到2.x 這個略麻煩.參考:https://artemisconsultinginc.com/blog/migrating-spring-boot-from-1-x-to-2-x/Spring Boot 2.0 Migration Guidehttps://docs.spring.io/spring-boot/docs/2.0.x/reference/html/appendix-dependency-versions.htmlhttps://docs.spring.io/spring-boot/docs/1.5.x/reference/html/appendix-dependency-versions.html
  • springboot 中classpath.idx 是什麼版本引入的.效果怎麼樣
  • 在 Spring Boot 中,classpath.idx 是一個索引檔案,用于優化類路徑的掃描和加載。該檔案包含了類路徑中所有 JAR 檔案中的類和資源的索引資訊,以及它們在 JAR 檔案中的位置。Spring Boot 應用程式在啟動時會使用該索引檔案來快速地定位和加載所需的類和資源,進而加快應用程式的啟動速度。
  • classpath.idx 檔案是從 Spring Boot 2.2 版本開始引入的,它是基于 Spring Framework 5.2 中的新特性而開發的。在早期版本的 Spring Boot 中,類路徑掃描是通過遞歸周遊類路徑中的每個 JAR 檔案來實作的,這種方式效率較低,特别是當應用程式的類路徑包含大量 JAR 檔案時會更加明顯。是以,引入 classpath.idx 索引檔案可以顯著提高 Spring Boot 應用程式的啟動速度。
  • 總的來說,如果你的 Spring Boot 應用程式的類路徑包含大量的 JAR 檔案,并且啟動速度比較慢,那麼使用 classpath.idx 索引檔案可以有效地提高應用程式的啟動速度。
  • —— 來自chatGPT
  • 使用Java的原生技術JarIndex技術. 為SpringBoot FatJar提供一個JarIndex檔案. 再重寫部分的SpringBoot ClassLoader相關邏輯. 以使其能夠在加載類的時候用到自建的JarIndex, 進而起到加速搜尋的目的.參考: 一些可以顯著提高大型 Java 項目啟動速度的嘗試 - 知乎 (連結2 - 今日頭條)
  • 使用原生的Class加載方法, 不使用SpringBootFatJar的 class 加載方式.這個是由于FatJar的加載方式決定的. 可以認為 Fatjar方式在檔案讀取上可能會有一些性能損耗. 同時在檔案查找效率上, 也可能比原生的低一些. 這個也是為什麼在2.x的 spring-boot-loader項目中對此部分進行了相應的索引加速優化.另外也有一些 springBoot官方的issue對此問題進行了讨論:https://github.com/spring-projects/spring-boot/issues/11207

    org.springframework.boot.loader.jar.Handler creates unnecessary garbage during URL normalisation #1120Investigate startup performance between main and jar #7493Startup performance differences between Spring Boot versions and main class vs jar file formats [SPR-14956] #19523https://jira.spring.io/browse/SPR-14956?redirect=false

以上三種方法,各有各的優點. 如果是最正統的做法,肯定是直接更新到2.x. 但是這個有成本不好實施. 如果使用jarindex這個實際就是2.x的官方已經有的實作. 是以也比較折騰最後可能沒有官方的好. 現在最省成本的辦法就是直接使用main啟動了. 這個可以參考 spring-boot-loader的launcher的代碼.

參考源碼: https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java

大概可以了解到其原理, 就是在spring啟動的時候先啟動的不是main class .而是啟動的 launcher類. 而launcher類會對使用改造過的 URLClassLoader 來完成對類的加載.

這個改造過的ClassLoader會對 SpringBootFatJar進行預處理. 對裡面已經存在的嵌套Jar包進行讀取與索引處理. 以便後後面在真正進行ClassLoad的時候,能夠查找到正确的類進行加載.

具體可以參考:

  • 三分鐘帶你了解SpringBoot真正的啟動引導類

使用MainClass直接啟動的原理與可行性:

并不是一開始我們就找到了官方的文檔說明. 這個是從我們的實際項目在Idea中調試時發現的時間差異. 最後對比發現是類的加載方式的差異造成的啟動速度的差異.

這裡有一個官方鏡像的相關說明: https://docs.spring.io/spring-boot/docs/current/reference/html/container-images.html

springboot 啟動速度調優

我們對比了某solar-xxx 服務在 macbookpro 2020 上的運作時間差異.

  • 使用java -jar xxx.jar 運作的速度大概是 120秒的啟動時間.
  • 把xxx.jar 解壓後直接使用 laucher類啟動. 其啟動時間幾乎沒有變化.
  • 直接啟動 MainClass , 此種啟動方法的啟動時間大概能提升到 60秒左右. 幾乎是一倍時間的差異.

繼續閱讀