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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...