在萬物雲原生下的環境下,Java的市場份額也因耗資源、啟動慢等缺點,導致在雲原生環境裡被放大而降低,通過這篇文章,讀者可以更好地瞭解如何在雲原生環境下通過升級相關版本和使用GraalVM打出原生映象到方式,優化Java應用的效能和資源利用率,使Java應用更好地適應雲原生環境。
現在我們的專案能正常執行,為什麼要耗費大量人力重構?
在雲原生時代,隨著技術的日新月異變化,我們尋找最佳技術解決方案的道路上總是充滿坎坷。作為分散式應用的主流技術,Kubernetes(簡稱k8s)和Java在這個時代交織出一段愛恨情仇般的佳話。那雲原生下這兩個技術究竟是敵是友,需要我們進行深入分析。首先,我們站在雲原生的角度來看,難以忽視的是k8s 的確被時代認可,尤其在微服務架構、容器化部署和資源管理等方面。其強大的自動化、彈性伸縮和容錯能力成為了很多軟體系統的最佳實踐。而Java作為一門擁有28多年曆史的程式語言,無論是在市場份額和生態圈都有無可撼動的地位。Java在分散式系統、中介軟體、業務邏輯處理等方面有著豐富的實踐經驗,為開發人員提供了良好的程式設計體驗和廣闊的生態支援。而在眾多應用架構的背後的技術架構都離不開Kubernetes和Java。從這個角度看來,它們似乎是一對黃金搭檔,但關係到具體實踐過程時,細節往往能決定成敗。我們來看看這兩個技術在實際操作中產生的愛恨情仇。
在雲原生架構中,k8s為應用提供了資源限制與管理的能力,使得應用能夠按需分配資源。然而,Java在處理資源方面存在一定的侷限性。因為JVM在記憶體管理方面依賴於Xms和Xmx等引數進行設定,導致應用在Kubernetes中的資源管理相對複雜。在這期間我們發現java也不甘示弱 在JVM層面對資源管理等方面也進行了改進 比如增加了Containters標誌,使得Java應用在k8s環境中的資源設定更為簡化。
在微服務架構中,服務發現和負載均衡是關鍵。過去,Java生態中有很多解決方案,例如Eureka、Zookeeper等,這讓java在服務發現和負載均衡方面有多種選擇,
但在k8s環境中,天然具備服務發現和負載均衡的能力,如果需要轉到雲原生就需要對微服務進行調整以適配k8s環境。
k8s在持續整合與持續部署方面具備很強的能力,與GitLab、Jenkins等CI/CD工具的結合可以為Java應用提供極致的開發、構建和部署體驗。通過將k8s和Java生態中優秀的CI/CD工具整合在一起,架構師能夠實現敏捷的開發、快速的迭代和高效的運維。
在k8s的世界裡,Java應用程式的部署和管理已然變得越來越高效。但隨著微服務、Serverless 架構的興起,Java的致命缺陷開始暴露出來。首先是啟動時間,Java應用程式在啟動時需要載入眾多類檔案,帶來了相對較長的啟動時間。再者,Java應用程式的記憶體佔用往往較高,這在雲原生環境中意味著更高的執行成本。然後我們開始尋找替代方案,以求擺脫Java在雲原生領域的束縛,進而擁抱更快、更輕量級的應用程式執行環境。
於是,GraalVM應運而生。GraalVM是一個高效能的Runtime,支援多種程式語言(這也是它通用的原因)。它提供了Java與雲原生領域的橋樑,可以將Java程式碼編譯成本地可執行檔案,這意味著更快的啟動速度和更低的記憶體使用。與之同時,GraalVM還提供了對Java的完整支援,便於將現有的Java應用程式遷移到雲原生領域。此外,GraalVM還整合了許多強大的特性,如 (AOT)編譯、即時編譯(JIT)以及強大的GC回收機制。這些特性使得GraalVM成為了在雲原生領域的新寵。
如果已經擁有一個功能完備且經過充分測試的Java應用程式,那麼將其轉換為native-image可以最大程度地複用現有程式碼庫,減少重新編寫應用程式所需的工作量和風險。此外我們部門專案都是Java,使用native-image能有效降低遷移成本。
如果隊伍中已經具備較為豐富的Java開發經驗,那麼將Java應用轉換為native-image會降低學習成本和培訓成本,相較於使用全新的程式語言來重寫應用,這會使得開發者更容易上手。
雖然Java應用在某些效能方面可能不如其他程式語言,但通過使用native-image,可以將Java應用轉換為與C或C++等編譯型語言類似的二進位制檔案,從而縮小效能差距。native-image為應用帶來更快的啟動速度和更低的記憶體佔用,而這在k8s這種容器化的環境中尤為重要。
Java具有良好的跨平臺相容性,而藉助GraalVM進行native-image編譯,我們依然可以保留這一特性。這為應用部署提供較高的靈活性,降低了環境遷移和維護的複雜度。
Java擁有龐大的開發社群和一個成熟、豐富的生態系統,提供了大量的庫和框架來支援各種應用的開發。使用native-image可以更好地利用這些資源,簡化應用的開發和維護過程。
採用jdk17+springboot3+native-iamge=原生輕量化服務
對於低頻流量的業務系統,採用試點專案的方式保證可行性的落地,根據業務功能進行專案合併,而對於合併後的全新專案,直接升級(jdk 和spring框架) version 完成專案合併後保證java版本的專案跑起來,之後採用native-image的方式打包成原生映象,讓專案在 新特性/穩定性的版本基礎上 晉升為真正的輕量化應用。
JDK 8 到 JDK 17 之間引入了許多新的語言特性和功能,如Lambda 表示式、Stream API、Optional 類、預設方法、介面私有方法等。這些特性可以使程式碼更簡潔、可讀性更好,並支援函數語言程式設計風格,提高開發效率和程式碼質量。
JDK 17 中進行了多項效能優化和增強,包括垃圾回收器的改進、JIT 編譯器的增強、並行類庫的改進等。這些優化可以提高應用程式的效能和吞吐量,特別是在大規模和高並行的情況下。
JDK 9 引入了模組化系統(Java Platform Module System,JPMS),它提供了更好的程式碼組織和隔離,使得複雜的應用程式可以更容易地維護和擴充套件。模組化系統還可以幫助識別和解決潛在的依賴衝突問題,提高應用程式的穩定性和可靠性。
JDK 17 在安全性方面有多項改進,包括加強的加密演演算法(SHA-3、X25519、Ed25519 )、更新的安全協定(TLS 1.3)、安全性增強的 API 等。這些改進可以提升應用程式的安全性,防範潛在的安全漏洞和攻擊。
JDK 17 提供了許多工具和效能分析的改進,例如 JShell(互動式程式設計工具)、Java Flight Recorder(JFR,低開銷的效能分析工具)、JDK Mission Control(JMC,效能監控和故障診斷工具)等。這些工具可以幫助架構師和開發團隊更好地分析和優化應用程式的效能和行為。
從 JDK 8 到 JDK 17,Java 平臺一直在持續改進和演進。JDK 17 是一個長期支援(LTS)版本,意味著它將獲得長期的支援和維護,包括安全更新、錯誤修復和效能改進。作為架構師,選擇升級到 JDK 17 可以獲得更穩定和可靠的平臺,並確保應用程式能夠得到長期的支援。
JDK 17 引入了許多新的功能、改進和效能優化。這些包括新的語言特性、增強的效能和安全性、更好的垃圾回收器、模組化系統等。通過將 Spring Boot 升級到版本3,可以利用 JDK 17 的新功能,提高應用程式的效能、穩定性和安全性。
Spring Boot 1.x 系列最初是為 JDK 6 和 JDK 7 開發的,而 Spring Boot 2.x 系列則對 JDK 8 有更好的支援。我們將 JDK8 升級到 JDK 17,將 Spring Boot 從版本1.x、2.x升級到版本3,可以確保應用程式能夠充分利用 JDK 17 的新特性,並獲得更好的相容性和效能。
隨著時間的推移,Spring Boot 的新版本會修復舊版本中存在的問題、漏洞和錯誤。通過升級到最新的 Spring Boot 版本,您可以獲得這些修復,並提高應用程式的穩定性和安全性。
隨著時間的推移,Spring Boot 社群會隨著新版本的釋出而更新和改進檔案、範例和社群支援。通過升級到最新版本,您可以獲得最新的檔案和社群支援,使您能夠更好地使用和維護 Spring Boot 應用程式。
JDK 在不同的版本中引入了新的 API,並對舊 API 進行了修改和棄用。在升級到 JDK 17 之前,需要仔細檢查你的程式碼,查詢並解決所有使用了已棄用的 API 的地方。還應該熟悉 JDK 9、JDK 11、JDK 14 和 JDK 17 之間的主要 API 變動,以便在升級過程中進行必要的修改 (servlet api沒報錯先忽略)。
1.JDK 9 引入了模組化系統,將 JDK 中的功能劃分為一組模組。如果你的應用程式依賴於 JDK 8 中不存在的模組,或者使用了模組化系統中的新特性(如模組路徑),在升級到 JDK 17 時需要進行相應的調整。確保你的應用程式的模組宣告和依賴關係與 JDK 17 相容,並進行必要的遷移工作(jdk8升的忽略)。
JDK 8 到 JDK 17 期間,JDK 團隊可能會移除一些不再支援的功能。在升級之前,檢視 JDK 的釋出說明和檔案,瞭解哪些功能將被移除或棄用。檢查你的程式碼是否使用了這些功能,以便及時進行修改和適應。
JDK 17 中引入了一些新的位元組碼指令和執行時優化,這可能會導致你的應用程式在升級後出現不同的行為。特別是在使用反射、位元組碼增強和動態代理等技術的情況下,需要仔細測試和驗證升級後的應用程式是否仍然正常執行。
升級 JDK 可能會對你的應用程式依賴的第三方庫和工具產生影響。在進行升級之前,確保你所使用的所有庫和工具與 JDK 17 相容,並及時更新到最新版本。此外,還要留意一些特定的庫或工具可能會有與 JDK 版本相關的限制或要求。
升級 JDK 可能會帶來效能改進和 bug 修復,但也可能引入新的效能問題或穩定性問題。在升級之後,通過全面的效能和穩定性測試,確保你的應用程式在新的 JDK 版。
如果程式碼使用了 JDK 8 中被移除或棄用的功能。查詢 JDK 17 的釋出說明,找到替代的功能或方法,並進行相應的修改。比如sun.misc.Unsafe 其大部分方法已被標記為 @Deprecated
在升級到 JDK 17 後,你的應用程式出現了意料之外的行為。 使用偵錯工具和紀錄檔記錄,對程式碼進行詳細調查,查詢與位元組碼和執行時差異相關的問題,並進行相應的修復。例如,JDK 17 引入了 instanceof
指令的新變體 instanceof<type>
,用於改進 instanceof
運運算元的效能。如果你的程式碼在 JDK 8 中使用了自定義的位元組碼操作,需要檢查是否需要相應地調整程式碼。
升級到 Spring Boot 3.x 可能需要更新你的應用程式的依賴項,如 Spring Framework、Hibernate 等。解決方法:檢視 Spring Boot 的檔案和依賴項管理,瞭解每個版本所需的依賴項版本,並更新你的專案組態檔 Maven 以使用相應的版本。確保所有依賴項與 Spring Boot 3.x 相容。
Spring Boot 3.x 可能引入了新的設定屬性或更改了現有屬性的名稱或行為。解決方法:仔細檢查你的應用程式的組態檔,特別是 application.properties
或 application.yml
,根據 Spring Boot 的檔案和遷移指南,更新和調整屬性的名稱和用法。
Spring Boot 3.x 可能修改了某些自動設定類或預設行為,可能會影響你的應用程式的行為。 檢視 Spring Boot 的升級檔案和釋出說明,瞭解哪些自動設定類發生了變化,並在必要時進行相應的修改和調整。
Spring Boot 3.x 可能引入了新的測試框架或更改了現有的測試註解和行為。 檢視 Spring Boot 的測試檔案,瞭解測試框架的更新和變化,並相應地修改和調整你的測試程式碼
在 Spring Boot 2.x 版本中,許多 Spring Boot Starter 的命名方式發生了變化。例如,原來的 spring-boot-starter-web
變為 spring-boot-starter-webflux
,spring-boot-starter-data-jpa
變為 spring-boot-starter-data-jdbc
等。Spring Boot 2.x 中,預設的快取自動設定使用 JCache(JSR-107)作為快取提供者在 Spring Boot 3.x 中,快取自動設定發生了變化。Spring Boot 3.x 引入了對 Caffeine 快取提供者的預設支援
在社群版和企業版本中,如果不指定GC ,預設都使用的是 Serial GC,而在企業版中可以指定使用 G1 垃圾回收器。G1 垃圾回收器是一種現代化的低延遲垃圾回收器,適用於大部分應用場景。而在在企業版中,引入了名為 GraalVM Native Image 的功能,它使用了不同的垃圾回收器。GraalVM Native Image 是一個 AOT(Ahead of Time)編譯器,可以將 Java 程式編譯成本地機器碼,從而提供更快的啟動時間和更低的記憶體佔用。對於 Native Image,GraalVM 企業版使用了垃圾回收器 Substrate VM,它是專門為 AOT 編譯的應用程式設計的。
GraalVM 企業版通常會提供更多的效能優化功能和選項。這包括對即時編譯(Just-in-Time Compilation)的改進、程式碼優化和程式分析等方面的增強。企業版的目標是提供更高效、更高效能的執行環境。
企業版可能提供一些額外的工具、庫和擴充套件,以滿足企業級應用程式的需求。這些工具可能包括效能分析器、偵錯程式、記憶體分析工具和效能監控等。
GraalVM 企業版通常會提供一些安全增強功能,以提供更好的應用程式安全性和保護。這可能包括對程式碼審計、漏洞掃描和安全加固方面的支援。
企業版可能提供更好的跨語言整合支援。GraalVM 企業版具有更廣泛的語言支援,包括 Java、JavaScript、Python、Ruby 等,使得在混合語言環境中開發和執行應用程式更加便捷。
企業版提供商業支援和服務,包括錯誤修復、安全更新和技術支援。這可以確保您在使用 GraalVM 時獲得及時的支援和幫助。
新版本的JDK和Spring Boot通常會引入效能改進和優化,包括記憶體管理、垃圾回收、執行緒處理等方面的優化。這可能會減少應用程式的記憶體佔用和CPU負載,從而提高系統的響應速度和吞吐量。再整合native-image 編譯生成的原生映象的啟動速度往往比JVM的啟動速度快得多,這對於有很多短暫任務的應用場景(例如serverless或者函數計算)非常有利
JDK的每個版本都在記憶體管理和垃圾回收方面進行了改進。新的版本通常會引入更高效的垃圾回收演演算法和記憶體管理策略,使應用程式能夠更有效地利用記憶體資源,並減少垃圾回收的停頓時間。這將有助於降低記憶體佔用和減少GC相關的CPU開銷,而採用原生映象後,通常具有比基於JVM的應用更低的記憶體佔用,這有利於降低執行時的資源消耗。
新版本的Spring Boot可能會引入更好的資源管理和優化機制。這包括對資料庫連線、執行緒池、快取等資源的更有效的管理和利用,以減少不必要的資源消耗,提高系統的資源利用率,而使用 native-image 編譯後的本地機器程式碼 更加顯著減小應用程式的記憶體佔用。因為 native-image 只包含應用程式所需的執行時元件和庫,而不需要整個 JVM 的額外開銷 映象大小能減少80%,且編譯後的本地機器程式碼不再依賴於 JVM, 減少應用程式對系統資源的消耗,使得應用程式可以在資源受限的環境中更高效地執行。
新版本的JDK和Spring Boot可能會提供更高效的並行處理機制和多執行緒程式設計模型。這將使應用程式能夠更好地利用多核CPU,並提高並行效能。通過更好的執行緒池管理、非同步程式設計模型等,可以減少不必要的執行緒開銷,提高系統的並行能力。
通過優化資源消耗,尤其是記憶體和CPU,可以降低硬體需求和成本。較低的記憶體佔用和CPU負載意味著可以在相同的硬體設定下執行更多的應用範例,從而節省硬體成本。此外,較低的資源消耗還可以減少運維成本和能源消耗。
JDK 17是一個長期支援版本(LTS),它將獲得長期的官方支援和維護,包括安全更新、錯誤修復和效能優化。這意味著我們的應用程式可以在未來幾年內持續受到支援,保持安全、穩定和可靠。
通過升級到最新的JDK和Spring Boot版本,可以跟上技術的演進和發展。利用新的語言特性、框架改進和工具,提高開發效率和程式碼質量。同時,升級也為我們團隊提供了學習和發展的機會,使他們能夠跟上行業的最新趨勢和技術標準,提高他們的技術能力和知識廣度。
隨著時間的推移,與JDK和Spring Boot相關的生態系統將持續發展和壯大。升級到最新版本可以使您能夠獲得更廣泛、更成熟的生態系統支援。這包括更多的第三方庫、工具、外掛和社群資源,可以加速開發過程、解決問題和提供更豐富的功能集。
新版本通常會引入更多的開發工具和改進,旨在提高開發效率和開發人員的體驗。例如,新的語言特性、API改進、自動化工具和開發工作流程的優化等,都有助於簡化開發任務、減少樣板程式碼,並提高團隊的整體生產力。
通過升級到最新版本,公司成員可以參與到技術升級的過程中,提出建議、貢獻經驗和共用知識。這有助於增強對公司的積極性和歸屬感,激發他們的職業發展動力,同時也為他們提供學習和成長的機會。
升級到最新的技術版本可以使部門甚至公司保持在技術的前沿,並具備更強的競爭力。公司將能夠應對新的技術挑戰和專案需求,提供更高質量、更高效能的解決方案,從而在市場上保持競爭優勢。
升級到較新的版本可以幫助您為未來做好準備。您的應用程式可以充分利用新的技術和功能,以滿足不斷變化的業務需求和使用者期望。同時,較新的版本通常會更好地適應新的硬體和部署環境,為您的應用程式提供更好的效能、可延伸性和可靠性。
總之,升級JDK和Spring Boot版本,以及使用native-image打出來的原生映象可以幫助Java應用更好地適應雲原生環境,提高應用的效能和可延伸性。這些措施可以幫助Java在雲原生環境中更好地發展
作者:京東科技 徐擁
來源:京東雲開發者社群