SkyWalking 8源码(一):Agent字节码增强的实现

准备

1、下载skywalking的开源代码到本地,根据文档进行编译:

Prepare JDK8+ and Maven 3.6+
Run ./mvnw clean package -DskipTests
All packages are in /dist.(.tar.gz for Linux and .zip for Windows).

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"));
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值