美文网首页
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