Sermant類隔離架構:解決JavaAgent場景類衝突的實踐

来源:https://www.cnblogs.com/huaweiyun/archive/2023/09/08/17687679.html
-Advertisement-
Play Games

Sermant是基於Java位元組碼增強技術的無代理服務網格,其利用Java位元組碼增強技術為宿主應用程式提供服務治理功能。 ...


本文分享自華為雲社區《Sermant類隔離架構解析——解決JavaAgent場景類衝突的實踐》,作者:華為雲開源。

Sermant是基於Java位元組碼增強技術的無代理服務網格,其利用Java位元組碼增強技術為宿主應用程式提供服務治理功能。因深知JavaAgent場景中類衝突問題會造成的影響,Sermant在設計之初便為此規划了全面的類隔離架構。經歷多次迭代,如今Sermant的類隔離架構已可以輕鬆的應對各種複雜的類載入環境。

一、JavaAgent場景為什麼要註意類衝突問題?

類衝突問題並非僅存在於JavaAgent場景中,在Java場景中一直都存在,該問題通常會導致運行時觸發NoClassDefFoundError、ClassNotFoundException、NoSuchMethodError等異常。

從使用場景來看,基於JavaAgent技術所實現的工具,往往用於監控、治理等場景,並非企業核心業務程式。如果在使用時引入類衝突問題,可能會造成核心業務程式故障,得不償失,所以避免向核心業務程式引入類衝突是一個JavaAgent工具的基本要求。

還有一個重要原因是在Java應用中可以於開發態採用依賴的升降級、統一依賴架構治理等手段解決該問題。但基於JavaAgent技術實現的工具作用於運行態,無法在開發態就和需要被增強的Java應用進行統一的依賴管理,所以引入類衝突問題的可能性更大。

二、JavaAgent場景如何解決該問題?

無論是在Java應用中,還是在JavaAgent場景,修複類衝突的邏輯都是一致的,就是避免引入會衝突的類。不同點在於基於JavaAgent技術實現的各式各樣的工具,往往都具有業務無關性,在設計和實現之初,並不會為特定的Java應用類型而定製。對於JavaAgent程式而言,需要被位元組碼增強的應用即是黑盒,所以無法像Java應用那樣去梳理依賴結構,排除、升級依賴項,統一進行依賴架構治理。並且JavaAgent往往在運行時使用,所以只能通過保障依賴絕對隔離的方式來避免引入衝突。

為何會產生類衝突,本文重點不在此,簡單講是因為我們在Java中因為重覆引入傳遞依賴、類的載入順序無法控制等問題,導致引入了相同【類載入器和全限定類名(Fully Qualified Class Name)都相同】但又表現不同【因為類版本不同而導致的類邏輯不一致】的類。所以為了避免衝突,我們就需要避免在運行時引入相同的類,如何讓JavaAgent引入的類和宿主完全不相同,從全限定類名和類載入器下手才是根本:

  • 基於maven shade plugin進行類隔離

該插件是Maven提供用於構建打包的插件,通過maven-shade-plugin的‘Relocating Classes’能力,來修改某些類的全限定類名。

cke_151.png

此方法的原理便是通過改變全限定類名來讓JavaAgent引入的類和Java程式的類完全不可能出現相同的情況,從根本上避免類衝突。但是我們在使用一種框架,或者使用一種產品時,往往約定要優於配置,基於maven-shade-plugin通過配置去改變全限定類名並不是一個簡單的辦法,在使用時就需要針對JavaAgent所涉及依賴進行梳理,在maven-shade-plugin中進行配置,並且需要在每次依賴變更後重新篩查。對持續迭代極不友好。

採用上述方法也對Debug造成阻礙,在Debug過程中被重定向的類的斷點將不可達,嚴重降低調試效率。

  • 基於類載入機制進行類隔離

基於maven-shade-plugin修改全限定類名往往用來解決單點的類衝突問題,雖然也能做到將JavaAgent所引入類完全隔離,但並不是一個好的解決方案。

基於類衝突原理,我們還可以通過限制兩個相同全限定類名的類的載入器來讓其不同,如Tomcat那樣,通過自定義類載入器破壞Java的雙親委派原則,來隔離JavaAgent引入的類。這樣既避免了繁重的配置,也避免了依賴變更而帶來的影響。但也有其弊端,在JavaAgent場景中往往會利用到Java應用程式的類,所以基於類載入器的隔離機制,往往就讓開發者只能通過反射等操作完成此類邏輯,這會對性能和開發效率產生不良影響。

三、Sermant如何做?

Sermant是基於Java位元組碼增強技術的無代理服務網格,不僅是一個開箱即用的服務治理工具,也同樣是一個易用的服務治理能力開發框架。

“把簡單留給別人,把麻煩留給自己!”

Sermant從設計之初就遵循上述重要原則,並規划了全方面的類隔離架構,利用Java的類載入機制對自身各模塊做了充分類隔離,讓使用者和開發者無需考慮因使用JavaAgent而導致類衝突問題,並且也針對開發者的使用場景做了優化,可以在開發中無縫使用被增強Java程式的類,避免因反射等行為帶來的不利影響。Sermant是如何實現的呢,下文將對Sermant類隔離架構進行詳細解析。

1) Sermant的類隔離架構解析

如上文所說,Sermant不僅是個開箱即用的服務網格,也同樣是一個易用的服務治理能力開發框架,服務治理能力是多樣的包括但不限於流量治理、可用性治理、安全治理等,所以Sermant採用插件化的架構來讓用戶能更靈活的接入和開發服務治理能力。

cke_152.png

在Sermant的整體架構下,我們不僅需要保證不向宿主服務引入類衝突問題,避免在開箱即用時對宿主服務造成負面影響,同時也需要保障框架與插件、插件與插件之間不會引入類衝突問題,避免插件開發者因為和其他服務治理插件產生類衝突問題而苦惱,所以Sermant設計瞭如下類隔離結構:

cke_153.png

  • SermantClassLoader,破壞雙親委派,用於載入Sermant框架核心邏輯,併在AppClassLoader下隔離出Sermant的類載入模型。避免受到宿主服務自身複雜類載入結構的影響,減少應對不同類載入結構服務的適配需求。
  • FrameworkClassLoader,破壞雙親委派,主要作用是隔離Sermant核心能力所引入的三方依賴,避免向宿主服務及服務治理插件引入類衝突問題。目前的主要場景 ①用於隔離Sermant的日誌系統,避免對宿主服務的日誌系統產生影響 ②隔離Sermant框架的核心服務(心跳、動態配置、統一消息網關)所需三方依賴。
  • PluginClassLoader,遵循雙親委派,主要用於隔離Sermant各服務治理插件,避免不同服務治理插件之間產生類衝突問題。
  • ServiceClassLoader,破壞雙親委派,主要用於隔離插件中的依賴,通過該類載入器載入插件服務的相關lib(插件服務會在插件載入時被Sermant初始化),開發者可任意引入三方依賴,無需關心對插件主邏輯的影響。

其中的PluginClassloader和ServiceClassloader不僅在類隔離中起到至關重要的作用,更是一種長遠的考慮,為每個插件設計獨立的類載入器,使得Sermant可以平滑的進行插件動態安裝&卸載以及插件熱更新。

2) 插件隔離的特殊之處

cke_154.png

在上文中所述類隔離架構中,可以看到一處特別的邏輯(紅框處),這也是Sermant中PluginClassLoader(插件類載入器)的特殊之處,在實際使用過程中,每個插件類載入器會在其中為每個線程維護一個局部的類載入器(localLoader)。

PluginClassLoader遵循雙親委派,在類載入過程中先委派SermantClassLoader載入Sermant的核心類,再通過自身載入插件類,當需要使用宿主服務的類時,則會委托局部類載入器(其Parent可以是任何類載入器,不局限於圖中所指示)進行載入。用於讓位元組碼增強的切麵邏輯(Sermant攔截器)可以獲取到宿主服務所使用的類,這有利於服務治理場景,其邏輯如下圖所示:

cke_155.png

通過重寫類載入器loadClass邏輯,在執行Sermant攔截器時,配置一個局部的類載入環境,讓Sermant攔截器中的邏輯可以順利的使用宿主服務載入的類,這樣開發服務治理插件時無需通過反射獲取宿主服務的類,從而提升服務治理能力的開發效率和最終運行時的性能,同時還避免了宿主服務和服務治理插件的類衝突。

(代碼實現可以在開源倉庫進行查看:)

3) 實戰效果如何

因接入JavaAgent而導致的依賴衝突、類衝突問題乃是業界通病,但如果有Sermant的類載入機制加持,該問題則可從根源避免,不再讓廣大JavaAgent的使用者和開發者深受其害!

《拜托,別在 agent 中依賴 fastjson 了》所述案例,是一個因JavaAgent而產生的依賴衝突問題的典型場景,其應用通過AppClassLoader載入到了Agent中fastjson的類FastJsonHttpMessageConverter, 該類依賴spring-web.jar的類GenericHttpMessageConverter,但由於AppClassLoader的搜索路徑中並沒有spring-web.jar(fastjson通過provide方式引入),最終載入類失敗。

但如基於Sermant開發則不會產生該問題,基於Sermant開發JavaAgent和Spring應用一起運行時的類隔離架構如下:

cke_156.png

在此類載入器的結構下,有兩個關鍵的不同:

  1. 由於Sermant改變了類載入的結構,通過Agent引入的fastjson已不在AppClassLoader的搜索路徑中,因此Agent中的FastJsonHttpMessageConverter類不再會被Spring應用通過AppClassLoader載入到,從根源上避免了文中所觸發的類衝突問題。
  2. 當運行時若Agent需要使用spring-web的類GenericHttpMessageConverter時,則可通過Sermant提供的局部類載入環境成功通過LaunchedUrlClassloader成功從Spring應用中獲取。

正是因為此兩點差異,讓基於Sermant開發的能力可以在和應用之間進行類隔離,避免通過JavaAgent引入類衝突問題,同時可以在運行時使用應用所引入的類。

四、總結

Sermant是基於Java位元組碼增強技術的無代理服務網格,其利用Java位元組碼增強技術為宿主應用程式提供服務治理功能。因深知JavaAgent場景中類衝突問題會造成的影響,Sermant在設計之初便為此規划了全面的類隔離架構。經歷多次迭代,如今Sermant的類隔離架構已可以輕鬆的應對各種複雜的類載入環境。

除了保證類隔離,Sermant作為服務網格需要重點關註自身的服務治理能力對宿主服務帶來的性能影響,所以也通過獨有設計避免因為過度隔離帶來的性能損耗。同時Sermant還在構建開放的服務治理插件開發生態,並提供高效的服務治理能力開發框架。在類隔離設計時也考慮到了易用性、開發效率提升等方面的問題。並未因為類隔離機制的存在,而降低開發的效率,增大學習曲線的陡峭程度。

Sermant 作為專註於服務治理領域的位元組碼增強框架,致力於提供高性能、可擴展、易接入、功能豐富的服務治理體驗,並會在每個版本中做好性能、功能、體驗的看護,廣泛歡迎大家的加入。

 

點擊關註,第一時間瞭解華為雲新鮮技術~

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ### 原型模式 #### 案例引入 ##### 克隆羊問題 有一隻羊,姓名為tom,年齡為1,顏色為白色,編寫程式創建和tom羊屬性完全相同的羊。 ##### 傳統方式解決 代碼實現 ```java public class Sheep { private String name; private ...
  • 結構體(簡稱struct)用於創建不同數據類型的成員集合,放入一個單一的變數中。雖然數組用於將相同數據類型的多個值存儲在單一變數中,但結構體用於將不同數據類型的多個值存儲在單一變數中。結構體對於將數據組合在一起以創建記錄非常有用。 聲明結構體 要在Go中聲明一個結構體,請使用type和struct關 ...
  • ### 前言 上篇文章 [13分鐘聊聊併發包中常用同步組件並手寫一個自定義同步組件](https://juejin.cn/post/7274475842998042665) 聊到併發包中常用的同步組件,並且還手把手實現了自定義的同步組件 本篇文章來聊聊併發包下的另一個核心-線程池 閱讀本文大概12分 ...
  • # Python初步瞭解裝飾器 - 裝飾器的概念 - 裝飾器的簡單使用 - 裝飾器的進階 - 裝飾器的練習 - 裝飾器的固定模塊 - 裝飾器的語法糖 ## 裝飾器的概念 ```python 裝飾器它不是一個新的知識點,它是有之前我們學習的名稱空間、函數嵌套、閉包函數等知識點彙總而來 器:工具 裝飾: ...
  • 在軟體開發中,我們經常會遇到這樣的情況:我們需要使用一個現有的類或者介面,但它與我們系統的目標介面不相容,而我們又不能修改它。這時候,我們該怎麼辦呢?大多數情況下我們都可以使用適配器模式來解決這個問題,**本文將從以下四個方面講解適配器模式**。 - 簡介 - 優缺點 - 應用場景 - Java 代 ...
  • ## 1. 什麼是Http2.0 HTTP/2.0,通常簡稱為HTTP/2,是一種用於傳輸超文本(例如網頁和資源文件)的網路協議。它是HTTP/1.1的繼任者,旨在提高性能和效率,以適應現代Web應用的需求。HTTP/2的主要特點包括以下幾點: 1. **多路復用(Multiplexing)**:H ...
  • 光看訪客這個名字,猜測這個訪客模式應該非常好理解,只要玩過Linux的人,都能深刻明白Root和非Root和訪客賬號登錄的巨大差別性。 # What is Visitor? 如果你沒玩過Linux,那麼假設公共的圖書館有一臺電腦,有兩個賬戶: - 其中一個是**管理員(Admin)**的賬戶,擁有這 ...
  • 作者:是奉壹呀 \ 來源:juejin.cn/post/7262274383287500860 看到一個評論,裡面提到了list.sort()和list.strem().sorted()排序的差異。 說到list sort()排序比stream().sorted()排序性能更好,但沒說到為什麼。 ! ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...