Sermant執行流程學習筆記,速來抄作業

华为云开发者联盟發表於2024-03-06

本文分享自華為雲社群《Sermant 的整體流程學習梳理》,作者:用友汽車資訊科技(上海)有限公司 劉亞洲 Java研發工程師。

一、sermant架構

Sermant整體架構包括Sermant Agent、Sermant Backend、Sermant Injector、動態配置中心等元件。其中Sermant Agent是提供位元組碼增強基礎能力及各類服務治理能力的核心元件,Sermant Backend、Sermant Injector、動態配置中心為Sermant提供其他能力的配套元件。

Sermant執行流程學習筆記,速來抄作業

二、java agent和bytebuddy組合使用場景

比較典型的就是skywalking、sermant、arthas、mockito。如果說java agent開了一扇門,那麼bytebuddy在開的這扇門中開啟了一片新的天地。

三、Sermant的入口

前面我們說AgentLauncher是java agent的入口,為什麼這麼說呢?

<manifestEntries>

    <Premain-Class>com.huaweicloud.sermant.premain.AgentLauncher</Premain-Class>

    <Agent-Class>com.huaweicloud.sermant.premain.AgentLauncher</Agent-Class>

    <Can-Redefine-Classes>true</Can-Redefine-Classes>

    <Can-Retransform-Classes>true</Can-Retransform-Classes>

</manifestEntries>

答案可以從pom.xml中找到答案,這裡可以看到基於Premain-Class和Agent-Class的兩個類都指向了AgentLauncher這個類。因此我們可以非常確認的肯定它就是javaagent入口類。類似於java程式有一個main的執行入口,而java agent有一個自己的入口類premain。

因此可以看到它的入口執行main:

    /**
     * premain
     *
     * @param agentArgs premain啟動時攜帶的引數
     * @param instrumentation 本次啟動使用的instrumentation
     */
    public static void premain(String agentArgs, Instrumentation instrumentation) {
        launchAgent(agentArgs, instrumentation, false);
    }

    /**
     * agentmain
     *
     * @param agentArgs agentmain啟動時攜帶的引數
     * @param instrumentation 本次啟動使用的instrumentation
     */
    public static void agentmain(String agentArgs, Instrumentation instrumentation) {
        launchAgent(agentArgs, instrumentation, true);
    }

基於premain模式的和基於agent模式,區別在於是否為isDynamic。從這裡我們可以看到這裡提出了兩個類值得我們去關注:AgentCoreEntrance、CommandProcessor,也即sermant這個專案的兩個重點類。

更多需要了解的,可以參考byte-buddy這個開源專案。

四、入口方法執行的全流程

Sermant執行流程學習筆記,速來抄作業

五、spi的載入過程

啟動核心服務的過程是spi的載入過程,此時會初始化所有的服務。也即我們看到的所有服務會在此時會做一個啟動的操作,同時還會啟動事件:

service.start();
collectServiceStartEvent(startServiceArray.toString());

其實這個兩個方法也做了很多事情。

啟動服務做的事情:

Sermant執行流程學習筆記,速來抄作業

collectServiceStartEvent則呼叫netty客戶端向netty服務端傳送資料。到服務端後,服務端進行資料處理,其收集的資訊提供給backend模組方便後臺展示檢視。

六、以標籤路由為例PluginService中擴充套件外掛初始化

除此之外,還有一批實現了BaseService介面的,也即PluginService擴充套件外掛服務基類,以標籤路由為例,可以看你的其初始化的整個過程。

Sermant執行流程學習筆記,速來抄作業

七、install的過程

同時我們可以看到install對應的process方法也是執行它的方法:

  public ResettableClassFileTransformer install(Instrumentation instrumentation) {
        AgentBuilder builder = new Default().disableClassFormatChanges();
        // 遍歷actions
        for (BuilderAction action : actions) {
            builder = action.process(builder);
        }
        // 執行安裝操作,此時交給bytebuddy
        return builder.installOn(instrumentation);
    }

從入參中的Instrumentation,我們往回看:ByteEnhanceManager.init(instrumentation)

這個方法裡面定義了action的順序。

 public static void init(Instrumentation instrumentation) {
        instrumentationCache = instrumentation;
        builder = BufferedAgentBuilder.build();

        // 初始化完成後,新增Action用於新增框架直接引入的位元組碼增強
        enhanceForFramework();
    }

執行下面的過程:

Sermant執行流程學習筆記,速來抄作業

我們根據上面的新增順序,來看初始化外掛的順序:

public static void enhanceDynamicPlugin(Plugin plugin) {
        if (!plugin.isDynamic()) {
            return;
        }
        // 獲取描述資訊
        List<PluginDescription> plugins = PluginCollector.getDescriptions(plugin);
        // 新增外掛,然後執行安裝操作
        ResettableClassFileTransformer resettableClassFileTransformer = BufferedAgentBuilder.build()
                .addPlugins(plugins).install(instrumentationCache);
        plugin.setClassFileTransformer(resettableClassFileTransformer);
    }

從引用上看,PluginSystemEntrance.initialize(isDynamic)中引用了這個方法。

Sermant執行流程學習筆記,速來抄作業

可以看到這裡的新增外掛,可以理解為自定義的外掛。

從sermant官網,我們可以知道:定義自定義外掛,需要實現PluginDeclarer這個介面。也即從這裡可以看到也即自定義的外掛:

 /**
     * 從外掛收集器中獲取所有外掛宣告器
     *
     * @param classLoader 類載入器
     * @return 外掛宣告器集
     */
    private static List<? extends PluginDeclarer> getDeclarers(ClassLoader classLoader) {
        final List<PluginDeclarer> declares = new ArrayList<>();
        for (PluginDeclarer declarer : loadDeclarers(classLoader)) {
            if (declarer.isEnabled()) {
                declares.add(declarer);
            }
        }
        return declares;
    }

有了外掛,就可以進行安裝操作。

按照這個順序,可以看到對應的action.process(builder)裡面也執行了對應的構建方法。完成構建後,執行installOn方法。

完成安裝工作後,根據安裝前spi的增強實現,然後執行下游服務攔截增強,從而實現精準篩選工作。

八、以標籤路由下游攔截處理為例

可以看到標籤路由對應的幾個代表性的Declarer:

NopInstanceFilterDeclarer、LoadBalancerDeclarer、BaseLoadBalancerDeclarer、ServiceInstanceListSupplierDeclarer等。

對應的攔截器Interceptor:

NopInstanceFilterInterceptor、LoadBalancerInterceptor、BaseLoadBalancerInterceptor、ServiceInstanceListSupplierInterceptor。

兩者相互照應。

LaneServiceImpl和LoadBalancerServiceImpl是基於sermant框架的外掛服務spi做的實現。

LaneServiceImpl和RouteRequestTagHandler是和路由能力相關的,LaneServiceImpl和LaneRequestTagHandler是和染色能力相關的。
RouteRequestTagHandler用來攔截並儲存呼叫過程中的標籤,FlowRouteHandler和TagRouteHandler是在路由選擇下游例項時做的篩選過程。

下游攔截方法會經過BaseLoadBalancerInterceptor到loadBalancerService.getTargetInstances(serviceId, instances, requestData),最終到 HandlerChainEntry.INSTANCE.process(targetName, instances, requestData),基於責任鏈模式執行處理。目前主要有兩種方式:FlowRouteHandler和TagRouteHandler。

Sermant執行流程學習筆記,速來抄作業

這裡面只是簡單的介紹了整體的流程,具體細節的內容,還需要自己多實踐。同時sermant大量使用了java agent的內容。

由於本人的侷限性,有不妥的地方,還望批評指正!

參考:

  • sermant官網: https://sermant.io/zh/
  • sermant開源地址:https://github.com/huaweicloud/Sermant
  • byte-buddy開源地址:https://github.com/raphw/byte-buddy

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

相關文章