1. 程式人生 > 其它 >SkyWalking 8原始碼(一):Agent位元組碼增強的實現

SkyWalking 8原始碼(一):Agent位元組碼增強的實現

技術標籤:java

準備

1、下載skywalking的開原始碼到本地,用idea開啟,將他的資料夾改成source檔案,否則無法除錯,會變成藍色:
在這裡插入圖片描述

maven自動下載包和外掛。

2、下載外掛translation可以翻譯英文註釋。

3、找到啟動檔案,ApplicationStartUp,在apm-webapp檔案下,是此專案的啟動檔案。

4、入口類 org.skywalking.apm.agent.SkyWalkingAgent

1、apm-sniffer包下的apm-agent包中的skywalkingagent類的premain方法為入口:

public static void
premain(String agentArgs, Instrumentation instrumentation) throws PluginException { ...... }

(1)agentArgs 是 premain 函式得到的程式引數,隨同 “– javaagent”一起傳入。
(2)instrumentation 是一個 java.lang.instrument.Instrumentation 的例項,由 JVM 自動傳入。
java.lang.instrument.Instrumentation 是 instrument 包中定義的一個介面,也是這個包的核心部分,
集中了其中幾乎所有的功能方法,例如類定義的轉換和操作等等。

skywalking利用了javaagent實現了虛擬機器級別的aop,利用位元組碼工具bytebuddy實現位元組碼增強。

2、初始化:

SnifferConfigInitializer.initializeCoreConfig(agentArgs); //讀取配置檔案資訊,環境變數資訊,vm option引數

3、載入外掛:

skywalking通過外掛載入機制,實現對不同中介軟體等的監控,即載入對應的外掛即可

pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins()); 

在PluginBootstrap類中的loadPlugins()中:

//先宣告一個資源載入器resolver,在resolver中的getResources()會查詢skywalking-plugin.def檔案中定義的類
PluginResourcesResolver resolver = new PluginResourcesResolver();
List<URL> resources = resolver.getResources();

//迴圈load skywalking-plugin.def ,並且解析該檔案
for (URL pluginUrl : resources) {
	try {
    	PluginCfg.INSTANCE.load(pluginUrl.openStream());
    } catch (Throwable t) {
    	logger.error(t, "plugin file [{}] init failure.", pluginUrl);
	}
}

//載入skywalking-plugin.def的類,儲存到list中
List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();
List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();
//通過反射建立這些類的例項
for (PluginDefine pluginDefine : pluginClassList) {
	try {
		logger.debug("loading plugin class {}.", pluginDefine.getDefineClass());
        AbstractClassEnhancePluginDefine plugin = (AbstractClassEnhancePluginDefine) Class.forName(pluginDefine.getDefineClass(), true, AgentClassLoader
                    .getDefault()).newInstance();
                plugins.add(plugin);
         } catch (Throwable t) {
                logger.error(t, "load plugin [{}] failure.", pluginDefine.getDefineClass());
       }
}

//最後返回plugins
return plugins;

將返回的plugins傳給pluginFinder類,作為構造引數,pluginFinder類中有方法buildMatch()來判斷需要修改哪些類的位元組碼(因為不是每一個類都需要增強):

//定義了集合
//pluginFinder將外掛分類儲存在兩個集合中,分別是:按名字分類和按其他輔助資訊分類
private final Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<AbstractClassEnhancePluginDefine>>();
    private final List<AbstractClassEnhancePluginDefine> signatureMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
    private final List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
	
	//構造方法
    public PluginFinder(List<AbstractClassEnhancePluginDefine> plugins) {
        for (AbstractClassEnhancePluginDefine plugin : plugins) {
            //抽象方法enhanceClass方法定義在外掛的抽象基類AbstractClassEnhancePluginDefine中,每一個外掛必須去實現這個類中的方法
            ClassMatch match = plugin.enhanceClass();  //故enhanceClass是每個外掛都會自己去實現的方法,指定需要增強的類

            if (match == null) {
                continue;
            }

            if (match instanceof NameMatch) {
                NameMatch nameMatch = (NameMatch) match;
                LinkedList<AbstractClassEnhancePluginDefine> pluginDefines = nameMatchDefine.get(nameMatch.getClassName());
                if (pluginDefines == null) {
                    pluginDefines = new LinkedList<AbstractClassEnhancePluginDefine>();
                    nameMatchDefine.put(nameMatch.getClassName(), pluginDefines);
                }
                pluginDefines.add(plugin);
            } else {
                signatureMatchDefine.add(plugin);
            }

            if (plugin.isBootstrapInstrumentation()) {
                bootstrapClassMatchDefine.add(plugin);
            }
        }
    }

//typeDescription是bytebuddy的內建介面,是對類的完整描述,包含了類的全類名
//傳入typeDescription,返回可以運用於typeDescription的類的外掛
public List<AbstractClassEnhancePluginDefine> find(TypeDescription typeDescription) {
        List<AbstractClassEnhancePluginDefine> matchedPlugins = new LinkedList<AbstractClassEnhancePluginDefine>();
        String typeName = typeDescription.getTypeName();
    	//根據名字資訊匹配查詢
        if (nameMatchDefine.containsKey(typeName)) {
            matchedPlugins.addAll(nameMatchDefine.get(typeName));
        }
		//通過除了名字之外的輔助資訊,在signatureMatchDefine集合中查詢
        for (AbstractClassEnhancePluginDefine pluginDefine : signatureMatchDefine) {
            IndirectMatch match = (IndirectMatch) pluginDefine.enhanceClass();
            if (match.isMatch(typeDescription)) {
                matchedPlugins.add(pluginDefine);
            }
        }

        return matchedPlugins;
    }

public ElementMatcher<? super TypeDescription> buildMatch() {
   		//設定匹配的規則,名字是否相同,通過名字直接匹配
        ElementMatcher.Junction judge = new AbstractJunction<NamedElement>() {
            @Override
            public boolean matches(NamedElement target) {
                return nameMatchDefine.containsKey(target.getActualName());
            }
        };
        judge = judge.and(not(isInterface())); //介面不增強,排除掉
    	//如果無法確定類的全限定名,則通過註解、回撥資訊等輔助方法間接匹配
        for (AbstractClassEnhancePluginDefine define : signatureMatchDefine) {
            ClassMatch match = define.enhanceClass();
            if (match instanceof IndirectMatch) {
                judge = judge.or(((IndirectMatch) match).buildJunction());
            }
        }
        return new ProtectiveShieldMatcher(judge);
}

以dubbo外掛為例:

public class WrapperInstrumentation extends ClassStaticMethodsEnhancePluginDefine { //間接繼承了基類
    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return new StaticMethodsInterceptPoint[] {
            new StaticMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named("makeWrapper");
                }

                @Override
                public String getMethodsInterceptor() {
                    return "org.apache.skywalking.apm.plugin.dubbo.patch.MakeWrapperInterceptor";
                }

                @Override
                public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override
    protected ClassMatch enhanceClass() {     //重寫了基類的enhanceClass()方法,指定了需要增強的類
        return byName("com.alibaba.dubbo.common.bytecode.Wrapper");
    }
}

4、通過bytebuddy中的agentBuilder來生成一個agent,並執行相關邏輯:

利用bytebuddy的API生成一個代理,並執行transform方法和監聽器Listener(主要是日誌相關)。

在premain中,通過鏈式呼叫,被builderMatch()匹配到的類都會執行transform方法,transform定義了位元組碼增強的邏輯:

agentBuilder.type(pluginFinder.buildMatch())
                    .transform(new Transformer(pluginFinder))
                    .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                    .with(new Listener())
                    .installOn(instrumentation);
private static class Transformer implements AgentBuilder.Transformer {
        private PluginFinder pluginFinder;

        Transformer(PluginFinder pluginFinder) {
            this.pluginFinder = pluginFinder;
        }

        @Override
        public DynamicType.Builder<?> transform(final DynamicType.Builder<?> builder,
                                                final TypeDescription typeDescription,
                                                final ClassLoader classLoader,
                                                final JavaModule module) {
            //typeDescription是bytebuddy的內建介面,是對類的完整描述,包含了類的全類名
            //這裡返回的是所有可以運用於typeDescription的類的外掛【一個類可能有多個外掛,見def檔案】
            List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
            if (pluginDefines.size() > 0) {
                DynamicType.Builder<?> newBuilder = builder;
                EnhanceContext context = new EnhanceContext();
                for (AbstractClassEnhancePluginDefine define : pluginDefines) {
                    DynamicType.Builder<?> possibleNewBuilder = define.define(  //最重要的define方法,在AbstractClassEnhancePluginDefine中定義
                            typeDescription, newBuilder, classLoader, context);
                    if (possibleNewBuilder != null) {
                        newBuilder = possibleNewBuilder;
                    }
                }
                if (context.isEnhanced()) {
                    LOGGER.debug("Finish the prepare stage for {}.", typeDescription.getName());
                }

                return newBuilder;
            }

            LOGGER.debug("Matched class {}, but ignore by finding mechanism.", typeDescription.getTypeName());
            return builder; //返回位元組碼
        }
    }

需要補充的是:

AbstractClassEnhancePluginDefine , 做為所有的外掛都要繼承實現的基礎, 抽象類AbstractClassEnhancePluginDefine 具體定義了什麼行為, 這些行為又有哪些意義?什麼地方在呼叫這個方法?

AbstractClassEnhancePluginDefine 中有四個方法

  • enhance 抽象方法, 抽象類中無實現, 子類去實現,具體的增強的邏輯
  • enhanceClass 抽象方法, 抽象類中無實現,子類去實現,具體的要增強的類的 Match
  • witnessClasses 也是可以被過載的一個方法,子類可過載
  • define 例項方法,它主要做了下面的幾次事 ( 入口方法)

1 找到增強外掛類的所有顯式指定的 WitnessClasses - 使用者自己過載顯式指定
2 呼叫 enhance 方法, 執行真正的外掛的增強邏輯,返回新的 DynamicType.Builder
3 設定上下文, 標記初始化定義步驟已完成
4 最後再返回新的 第 2 中的增強後的 newClassBuilder

public abstract class AbstractClassEnhancePluginDefine {
    private static final ILog LOGGER = LogManager.getLogger(AbstractClassEnhancePluginDefine.class);
   
    public DynamicType.Builder<?> define(TypeDescription typeDescription, DynamicType.Builder<?> builder,
        ClassLoader classLoader, EnhanceContext context) throws PluginException {
        String interceptorDefineClassName = this.getClass().getName();
        String transformClassName = typeDescription.getTypeName();
        if (StringUtil.isEmpty(transformClassName)) {
            LOGGER.warn("classname of being intercepted is not defined by {}.", interceptorDefineClassName);
            return null;
        }

        LOGGER.debug("prepare to enhance class {} by {}.", transformClassName, interceptorDefineClassName);

        /**
         * find witness classes for enhance class
         */
        String[] witnessClasses = witnessClasses();
        if (witnessClasses != null) {
            for (String witnessClass : witnessClasses) {
                if (!WitnessClassFinder.INSTANCE.exist(witnessClass, classLoader)) {
                    LOGGER.warn("enhance class {} by plugin {} is not working. Because witness class {} is not existed.", transformClassName, interceptorDefineClassName, witnessClass);
                    return null;
                }
            }
        }

        DynamicType.Builder<?> newClassBuilder = this.enhance(typeDescription, builder, classLoader, context);

        context.initializationStageCompleted();
        LOGGER.debug("enhance class {} by {} completely.", transformClassName, interceptorDefineClassName);

        return newClassBuilder;
    }

    protected abstract DynamicType.Builder<?> enhance(TypeDescription typeDescription,
        DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader, EnhanceContext context) throws PluginException;

    protected abstract ClassMatch enhanceClass();

    protected String[] witnessClasses() {
        return new String[] {};
    }

    public boolean isBootstrapInstrumentation() {
        return false;
    }

    public abstract ConstructorInterceptPoint[] getConstructorsInterceptPoints();

    public abstract InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints();

    public abstract StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints();
}

這個抽象基類中,最為重要的一個方法 define(),被premain中的transform呼叫,從而實現外掛中的攔截邏輯。

5、啟動服務

skywalking採用外掛架構,為了規範所有外掛,外掛都必須實現BootService這個介面,如下:

//BootService中宣告的生命週期方法,agent-core核心會在適當的時機呼叫不同的生命週期方法
public interface BootService {
    void prepare() throws Throwable;

    void boot() throws Throwable;

    void onComplete() throws Throwable;

    void shutdown() throws Throwable;
}

在premain方法的最後,會boot啟動服務:

		try {
            ServiceManager.INSTANCE.boot();
        } catch (Exception e) {
            LOGGER.error(e, "Skywalking agent boot failure.");
        }

boot類是ServiceManager類下的方法,開啟boot方法:

public void boot() {
        bootedServices = loadAllServices();

        prepare();
        startup();
        onComplete();
    }

<1>首先是loadAllService方法:

private Map<Class, BootService> loadAllServices() {
        Map<Class, BootService> bootedServices = new LinkedHashMap<>();
        List<BootService> allServices = new LinkedList<>();
        load(allServices);  //load方法,採用SPI機制實現
        for (final BootService bootService : allServices) {
            Class<? extends BootService> bootServiceClass = bootService.getClass();
            //服務是否有註解@DefaultImplementor,預設實現
            boolean isDefaultImplementor = bootServiceClass.isAnnotationPresent(DefaultImplementor.class);
            if (isDefaultImplementor) {
                if (!bootedServices.containsKey(bootServiceClass)) {
                    bootedServices.put(bootServiceClass, bootService);
                } else {
                    //ignore the default service
                }
            } else {
                //服務是否有註解@OverrideImplementor,覆蓋實現
                OverrideImplementor overrideImplementor = bootServiceClass.getAnnotation(OverrideImplementor.class);
                if (overrideImplementor == null) {
                    if (!bootedServices.containsKey(bootServiceClass)) {
                        bootedServices.put(bootServiceClass, bootService);
                    } else {
                        throw new ServiceConflictException("Duplicate service define for :" + bootServiceClass);
                    }
                } else {
                    Class<? extends BootService> targetService = overrideImplementor.value();
                    if (bootedServices.containsKey(targetService)) {
                        boolean presentDefault = bootedServices.get(targetService)
                                                               .getClass()
                                                               .isAnnotationPresent(DefaultImplementor.class);
                        if (presentDefault) {
                            bootedServices.put(targetService, bootService);
                        } else {
                            throw new ServiceConflictException(
                                "Service " + bootServiceClass + " overrides conflict, " + "exist more than one service want to override :" + targetService);
                        }
                    } else {
                        bootedServices.put(targetService, bootService);
                    }
                }
            }

        }
        return bootedServices;
    }
void load(List<BootService> allServices) { //load方法, ServiceLoader.load(BootService.class, AgentClassLoader.getDefault()))是典型的SPI機制
        for (final BootService bootService : ServiceLoader.load(BootService.class, AgentClassLoader.getDefault())) {
            allServices.add(bootService);
        }
    }

擴充套件:Java SPI思想梳理 - KKys的文章 - 知乎 https://zhuanlan.zhihu.com/p/28909673

6、最後一 步,註冊關閉鉤子

設定一個關閉鉤子,在JVM關閉時,建立一個執行緒,呼叫ServiceManager中的shutdown方法,進行資源釋放等。

Runtime.getRuntime()
                .addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread"));