美文网首页
Skywalking源码研究之Agent初始化

Skywalking源码研究之Agent初始化

作者: pq217 | 来源:发表于2024-08-09 13:21 被阅读0次

    微内核架构

    skywalking-agent采用微内核架构,本身的core代码比较轻量级,对各种技术的监控支持是通过各个插件Plugin实现的,例如:

    • dubbo-3.x-plugin(针对dubbo3的支持)
    • mvc-annotation-5.x-plugin(针对spring-mvc5的支持)
    • mysql-8.x-plugin(针对mysql8的支持)

    大部分的插件是skywalking内置的,可以支持绝大多数场景,还有一部分社区开发的各种插件,同时,可以开发自定以插件来支持特殊的场景

    javaagent

    jdk1.5后新增了类java.lang.instrument.Instrumentation,它提供在运行时重新加载某个类的的class文件的api,有了它就可以实现在程序运行前加入切点,独立与目标程序,侵入性比普通的AOP编程低很多

    使用方式如下:

    • 新建代理程序
    • 代理程序入口微premain而不是原来的main,方法包含参数Instrumentation
    • 代理程序加入自定义转换器(操作Instrumentation修改字节码)
    • 修改目标程序启动方式,加入-javaagent参数指向代理程序

    premain写法类似如下

    public static void premain(String agentArgs, Instrumentation inst) {
        ... // 使用inst修改字节码,实现自定义转换
    }
    

    由于Instrumentation写起来非常困难,所以有一个交互性更好的类库来实现切面编程,就是bytebuddy

    bytebuddy

    bytebuddy提供了一套非常友好的修改java字节码的API,有了它就可以非常轻松的实现对方法切面编程,类似spring-aop其到的作用

    javaagent+bytebuddy配合就可以轻松实现无侵入式的切面编程,写法类似如下(拦截spring的请求):

    public static void premain(String agentArgs, Instrumentation inst) {
        try {
            // 拦截spring controller,使用bytebuddy的builder
            AgentBuilder.Identified.Extendable agentBuilder = new AgentBuilder.Default()
                    // 拦截@Controller 和 @RestController的类
                    .type(ElementMatchers.isAnnotatedWith(ElementMatchers.named("org.springframework.stereotype.Controller")
                            .or(ElementMatchers.named("org.springframework.web.bind.annotation.RestController"))))
                    .transform((builder, typeDescription, classLoader, javaModule) ->
                            // 拦截 @RestMapping 或者 @Get/Post/Put/DeleteMapping
                            builder.method(ElementMatchers.isPublic().and(ElementMatchers.isAnnotatedWith(
                                            ElementMatchers.nameStartsWith("org.springframework.web.bind.annotation")
                                                    .and(ElementMatchers.nameEndsWith("Mapping")))))
                                    // 拦截后交给 SpringControllerInterceptor 处理
                                    .intercept(MethodDelegation.to(SpringControllerInterceptor.class)));
            // ByteBuddy只是帮我们更方便的操作Instrumentation
            agentBuilder.installOn(inst);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    其中SpringControllerInterceptor就是自定以的bytebuddy拦截器,比如要打印每个请求执行时间,写法类似如下(与AOP很像)

    public class SpringControllerInterceptor {
        @RuntimeType
        public static Object intercept(@Origin Method method,
                                       @AllArguments Object[] args,
                                       @SuperCall Callable<?> callable) {
            long start = System.currentTimeMillis(); // 开始时间
            try {
                Object res = callable.call(); // 调用原方法
                return res;
            } catch(Exception e) {
                log.error("controller error: ", e);
                return null;
            } finally {
                long end = System.currentTimeMillis();
                log.info("after controller execute in {} ms", end - start); // 输出程序执行时间
            }
        }
    }
    

    skywalking-agent初始化

    skywalking-agent就是使用javaagent+bytebuddy实现无侵入的切面编程,其premain核心代码如下:

    public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
        final PluginFinder pluginFinder;
        // 初始化配置,读取agent.config配置文件
        SnifferConfigInitializer.initializeCoreConfig(agentArgs);
    
        // 加载所有插件
        pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
    
        // 使用bytebuddy
        final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
        
        // 使用bytebuddy的builder
        AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).ignore(
                nameStartsWith("net.bytebuddy.");
    
        agentBuilder.type(pluginFinder.buildMatch()) // 根据插件生成类匹配规则
                    .transform(new Transformer(pluginFinder)) // 同样根据插件生成转换器
                    .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                    .with(new RedefinitionListener())
                    .with(new Listener())
                    .installOn(instrumentation); // ByteBuddy只是帮我们更方便的操作Instrumentation
    
        ServiceManager.INSTANCE.boot(); // 初始化所有服务类,类似spring的容器初始化
    }
    

    总结skywalking启动的核心逻辑:
    读取所有插件,根据插件的定义来确定如何拦截,并使用ByteBuddy实际进行拦截

    img.png

    有关插件的具体介绍,参照skywalking-agent插件

    相关文章

      网友评论

          本文标题:Skywalking源码研究之Agent初始化

          本文链接:https://www.haomeiwen.com/subject/eotkkjtx.html