領先一步
VMware 提供培訓和認證,以加速您的進展。
了解更多隨著 Spring Framework 6.1 和 Spring Boot 3.2 即將正式發佈,我們想分享 Spring 團隊正在進行的幾項努力的概觀,這些努力旨在讓開發人員能夠最佳化其應用程式的執行期效率。
我們將涵蓋以下技術和使用案例
如果您偏好觀看影片而不是閱讀部落格文章,我們推薦 Devoxx Belgium 2023 的「Spring Framework 6:策略主題」簡報
讓我們先從最重要的問題開始:我們為何應該關心改善雲端工作負載的執行期效率? 第一個原因可能是成本最佳化。我們都希望以更便宜的方式執行應用程式。更便宜的託管通常意味著使用更少的 CPU、更少的記憶體、更少的資源,這使我們的工作負載更具永續性。我們也身處一個您的應用程式可能會以某種方式涉及 Kubernetes 和容器的世界,這通常需要格外注意 Java 虛擬機器的啟動時間、預熱時間和記憶體管理。
Spring 團隊的目標是提供各種選項(其中一些可以組合使用),以最佳化數百萬個生產環境中 Spring 工作負載的執行期佔用空間和可擴展性。我們的目標是盡可能減少您的 Spring 應用程式中所需的變更量,以利用這些改進,但當然通常會涉及權衡,我們將盡可能明確地說明這些權衡。 希望這能為您提供足夠的資訊,讓您更清楚地了解這如何應用於您的組織、您的應用程式,並了解哪些權衡對您的環境而言是值得的。
利用這些執行期效率改進的一個常見要求是升級到 Spring Boot 3,其基於 Spring Framework 6,後者以 Java 17 為基準,並且需要從 Java EE (javax
套件) 轉換到 Jakarta EE (jakarta
套件)。當您進行此類升級時,將會向您提供一組新的執行期效率功能。
讓我們先從剛發佈的一項技術開始,該技術自 Java 21 起可用。虛擬執行緒旨在降低以簡單且流行的每個請求執行緒樣式編寫的伺服器應用程式的成本,以接近最佳硬體利用率進行擴展。
虛擬執行緒使 I/O 上的阻塞變得廉價,因此非常適合 Servlet 堆疊上的 Spring Web MVC 應用程式。 Spring MVC 可以充分利用 Tomcat 或 Jetty 上具有虛擬執行緒設定的那些新的執行期特性。 這在大多數使用案例中不需要程式碼變更,並且自然地適應以提供最佳效能,而無需微調執行緒池配置。
我們也聽到了 Spring 社群的反饋,要求我們不僅要給予維護模式下的 RestTemplate 和反應式 WebClient 之間的選擇。 因此,我們決定在 Spring Framework 6.1 中引入一個名為 RestClient
的「虛擬執行緒友善的現代 HTTP 客户端」(當然,即使沒有虛擬執行緒也是一個有吸引力的選項)。 Spring Cloud Gateway 和 Spring 產品組合中的相關基礎架構可以同樣受益於虛擬執行緒設定以及 Spring MVC,從而提供一致的整體體驗。
那麼,這對 WebFlux 和反應式堆疊意味著什麼?
我們有意選擇使用不同的阻塞和反應式堆疊,以便充分利用 WebFlux 伺服器中的反應式,並使 Spring Web MVC 堆疊(迄今為止在 start.spring.io 上最常用的 Web 堆疊)盡可能精簡,並採用常規阻塞執行緒架構。 Servlet 容器上的 Spring MVC 非常適合虛擬執行緒,作為改進傳統 Web 應用程式可擴展性的有吸引力的解決方案。 另一方面,WebFlux 伺服器提供最佳化的反應式堆疊,非常適合 Netty I/O 設定,透過不同的程式設計模型提供同等的執行期優勢。
當您需要應用程式層級的並行性時(例如,發送多個遠端 HTTP 請求,可能是串流的,並組合結果),Project Loom 結構化並行性未來可能會提供一個有趣的底層建構塊,但這不是開發人員在 Spring 應用程式中通常需要的 API 類型(並且它仍處於預覽階段)。 對於這種使用案例,WebFlux 和反應式 API(如 Reactor)目前具有無與倫比的附加價值,以及 Kotlin 協程及其 Flow
類型,後者提供了命令式和宣告式程式設計模型的有趣組合。 RSocket 是反應式互動模型的另一個絕佳附加價值的範例。
請注意,您不必選擇其中一個,因為 Spring MVC 也提供可選的反應式支援。 因此,如果您只需要在伺服器應用程式中的幾個使用案例中處理並行性,您可以簡單地使用具有虛擬執行緒設定的 Spring MVC 堆疊,並在您的 Web 控制器中無縫包含例如反應式 WebClient
互動,Spring MVC 會將反應式傳回值調整為 Servlet 非同步回應。 Spring MVC 中的這種反應式支援是完全可選的,只有在使用反應式端點時才需要在堆疊中使用 Reactor 和 Reactive Streams,並且 HTTP 堆疊基於 Servlet 容器,例如 Tomcat 或 Jetty(而不是 Netty)。
對於典型的 Web 應用情境,我們期望虛擬執行緒將成為 Spring 開發人員在 Java 21+ 上使用 Spring MVC 作為精簡 Web 伺服器堆疊的常見選擇。 更廣泛的 Java 生態系統仍需完全適應虛擬執行緒,例如避免在常見的 JDBC 驅動程式實作中進行任何執行緒釘選,但即使這樣也預計很快就會解決。 請確保使用 Spring Boot 3.2 或更高版本,將屬性 spring.threads.virtual.enabled
設定為 true
,並使用可用的最新程式庫和驅動程式版本來評估虛擬執行緒。
我們繼續完善 Spring Boot 3 中引入的 GraalVM 原生支援。 主要使用案例是使用 Buildpacks 建構最佳化的容器映像檔,其中包含微小的作業系統基礎層和您的應用程式,該應用程式已透過 Spring AOT(預先編譯)轉換和 GraalVM native image 編譯器編譯為原生可執行檔。 不需要 JVM 發行版。
這允許部署微小的容器,這些容器在數十毫秒內啟動(通常比常規 JVM 上的啟動時間快 50 倍),並為您的應用程式基礎架構降低記憶體消耗,並立即提供峰值效能。
GraalVM 非常密切地關注新的 Java 功能,例如,已經提供一流的虛擬執行緒支援:請參閱 Josh Long 最近的 All together now 部落格文章。
與 JVM 相比,GraalVM 出色的執行期特性是透過不同的權衡實現的。 原生映像檔編譯需要數分鐘而不是數秒。 它需要額外的元數據,以便正確處理反射、代理和 JVM 的其他動態行為。 Spring 推斷了許多這些元數據,但任何真實專案都可能需要一些額外的提示才能正常運作(例如,針對您的組織相依性)。 最後,Spring AOT 轉換和 GraalVM native image 的組合要求我們在建構時凍結類別路徑和 Spring Boot bean 條件。 您通常可以變更執行期配置中資料庫的 URL 或密碼,但不能變更資料庫類型或執行會變更 Spring bean 結構的操作。
從歷史上看,另一個缺點是由於缺乏即時編譯而導致的峰值效能有限,但在 GraalVM 免費條款與條件許可證下發佈 Oracle GraalVM(請參閱 相關限制)挑戰了這一假設。 您可以訂閱 此相關的 Buildpacks RFC 以追蹤其潛在的即將到來的支援,並且您已經可以使用 這個簡單的 Dockerfile
作為起點,在您的 Spring Boot 工作負載中嘗試它。
憑藉即時啟動和立即提供的峰值效能,Spring Boot 原生應用程式可以擴展至零。 讓我們探索一下這意味著什麼。
擴展至零是無伺服器的一種通用化。 工作負載不僅可以部署到無伺服器雲端平台,還可以部署到任何 Kubernetes 或雲端平台,這些平台在沒有要處理的請求時提供擴展至零的功能。 透過 Kubernetes,您可以使用 Knative 或 KEDA 等解決方案來擴展至零。 而且您不限於函數,您可以將任何類型的應用程式、任何類型的程式設計模型(包括傳統 Web 應用程式)擴展至零。 無伺服器最重要的特性不是技術性的,而是它實現的隨用隨付計費模式。
在各種使用案例中,擴展至零可能很有趣。 JVM 在開發高流量網站方面非常出色,但老實說,我們也開發了許多小型後端辦公應用程式,這些應用程式通常不是一直使用的。 當沒有人使用它們時,我們為什麼要付費? 還有臨時環境,通常只需要在一小部分時間內啟動,以及微服務,在大多數時間裡,快取允許關閉它們中的一些。 而且,別忘了高可用性,這迫使我們始終為每個服務維護兩個實例,以應對緊急情況,因為我們的應用程式啟動時間太長,無法從危害中恢復。
但是,對於無法接受 GraalVM native image 所需權衡的專案,如何擴展至零?
CRaC 是一個 OpenJDK 專案,它定義了一個新的 Java API,允許您在 HotSpot JVM 上檢查點和還原應用程式,該專案由 Azul Systems 開發,同時也受到 AWS Lambda 和 IBM OpenLiberty 的支援。 它基於 CRIU,這是一個在 Linux 上實作檢查點/還原功能的專案。
原理如下:您幾乎像往常一樣啟動應用程式,但使用已啟用 CRaC 的 JDK 版本。 然後在某個時候,可能在執行某些工作負載(這些工作負載將透過執行所有常見程式碼路徑使您的 JVM 升溫)之後,您可以使用 API 呼叫、jcmd 命令、HTTP 端點或其他機制觸發檢查點。
正在運行的 JVM 的記憶體表示形式(包括其溫度)隨後被序列化到磁碟,從而允許在稍後的時間點非常快速地還原,可能在具有類似作業系統和 CPU 架構的另一台機器上。 還原的處理程序保留了 HotSpot JVM 的所有功能,包括執行時的進一步 JIT 最佳化。
有趣的是,注意到「檢查點」和「還原」與 Spring 應用程式上下文生命週期的停止和啟動階段非常吻合。 Spring Framework 6.1 CRaC 支援主要關於將 CRaC 和 Spring 生命週期映射在一起,其餘的支援與 CRaC 無關,主要關於旨在正確關閉和重新建立 Socket、檔案和池的 Spring 生命週期改進。 除了常規的啟動和停止生命週期之外,此處的目標是支援多個停止和重新啟動週期。
與 GraalVM 一樣,Project CRaC 允許應用程式擴展至零,即使在小型伺服器上也能在幾十毫秒內即時啟動。 這比常規 JVM 冷啟動快 50 倍,並且與 GraalVM native image 類似。 但讓我們探索所涉及的權衡。
第一個權衡是 CRaC 要求您提前啟動應用程式,然後再投入生產環境。 那麼您應該在 CI/CD 平台上啟動它嗎? 帶或不帶您的生產遠端服務? 這引發了一系列非同小可的問題。
第二個權衡是需要關閉任何涉及 Socket、檔案和池的功能,然後根據 CRaC 生命週期正確地重新建立這些資源。 Spring Boot 為您處理 支援範圍。 但是某些程式庫根本尚不支援,因此可能需要一些時間才能完全支援您的整個堆疊。
我們認為第三個權衡是最棘手的。 建立自我包含的、隨時可以還原的容器映像檔可能很誘人。 但是在檢查點啟動期間載入到記憶體中的任何機密都將序列化到快照檔案中,從而洩露潛在的敏感資訊,例如您的生產資料庫密碼。
一個潛在的解決方案可能是執行不帶生產環境配置的檢查點啟動,並在還原時更新您的應用程式配置。 可以使用 Spring Cloud Context 和 @RefreshScope 註釋 來做到這一點。 Spring 團隊未來可能會探索這個主題,看看更多內建支援是否有意義。 您也可以採用策略,直接在您的 Kubernetes 平台上加密磁碟區上建立和儲存快照檔案,即使這需要更深入的平台整合。
最後一個關鍵特性是 CRaC 是 Linux 特有的,並且需要一些 Linux 功能微調才能在沒有特權模式的情況下運作。
請記住,我們正處於 Project CRaC 的早期階段,並且 Spring Boot 3.2 是第一個支援它的版本。 隨著檢查點還原技術與 Spring 的支援一起發展,其中一些限制可能會被解除。 如果您想親自嘗試此技術,請查看 Spring Framework 相關文件 和 https://github.com/sdeleuze/spring-boot-crac-demo。
我們已經看到了兩種讓您的 Spring 工作負載透過 GraalVM 和 CRaC 擴展至零的方式,但都涉及非同小可的權衡。 如果有另一種方法可以在更少限制下改善 Spring Boot 執行期特性,那會怎麼樣?
您可能聽說過 Project Leyden,這是一個新的 OpenJDK 專案,旨在改善 Java 程式的啟動時間、達到峰值效能的時間和佔用空間。 如果您想了解更多資訊,我們建議觀看 Brian Goetz 本人關於 此相關演講。
Project Leyden 最近引入了「premain」最佳化(基本上是 類別資料共享 + AOT 加強版),有趣的是,Java Platform 團隊發現了與 Spring 預先編譯最佳化的絕佳協同作用,最初創建該最佳化是為了允許 GraalVM native image 支援,但已經能夠在 JVM 上提供快 15% 的啟動時間。
雖然「premain」最佳化是高度實驗性的(目前它是 GitHub 上 Leyden 儲存庫的實驗性分支),但 Spring 團隊最近已能夠透過結合 JVM 上的 Spring AOT 和 Project Leyden 的那些最佳化,測量到 Spring Petclinic 範例應用程式的啟動時間快 2 倍到 4 倍,以及更快的預熱速度,幾乎沒有任何權衡。
以它們目前的形式,與 GraalVM 和 CRaC 不同,這些最佳化無法實現擴展至零,因為它們不允許應用程式在生產環境中以數十毫秒啟動。 但是,如果我們在幾乎沒有任何限制的情況下顯著改善 JVM 啟動和預熱時間,它就有可能成為主流,並與您可以按需選擇的其他即將推出的 Leyden 功能結合使用。
我們很高興分享,我們已經開始 Java Platform Group 和 Spring 團隊之間的合作,以了解我們可以將使用 Project Leyden 的 premain 方法的可能性推向多遠。 結合專為 JVM 設計的 Spring AOT 改進,我們期望進一步最佳化適用於廣泛的 Spring 應用程式。 我們將在未來幾個月內分享更多資訊。
如果您想親自嘗試,請查看 https://github.com/sdeleuze/spring-boot-leyden-demo 儲存庫。
聆聽來自世界各地 Spring 社群的反饋已被證明是 Spring 團隊靈感的重要來源,以及與 Oracle、Bellsoft、Azul 和許多其他公司務實合作的重要來源。
我們正努力支援這些新功能,同時最大限度地減少對 Spring 應用程式開發的影響,為眾多類型的應用程式提供直接的升級路徑。 這是我們策略性基礎架構工作中最具挑戰性但也最有價值的部分。
最後但並非最不重要的一點是,我們正在尋求關於您對您的組織和您的專案最感興趣的反饋。 您是否認為擴展至零和隨用隨付計費模式值得 GraalVM 或 CRaC 所需的權衡? GraalVM native image 提供的減少記憶體消耗對您來說是一個關鍵優勢嗎? 您是否認為 JVM 上結合 Project Leyden 的 Spring AOT 具有很高的潛力? 您對虛擬執行緒的看法是什麼? 請告訴我們!