源码角度了解Skywalking之启动源码分析

Skywalking的使用

对于Skywalking的使用,有四种配置方案

探针配置

配置如下:

-javaagent:/xxx/skywalking-agent.jar=agent.service_name=demo-provider

skywalking-agent.jar是Skywalking的核心jar包,它可以将收集到的数据信息发送到OAP中进行处理

JVM配置

使用JVM配置

-Dskywalking.agent.service_name = demo-provider

配置文件

Skywalking的安装包中有个config文件夹下有个agent.config文件,修改agent.service_name=${SW_AGENT_NAME:demo-provider}来配置对应的服务,如果系统环境变量中配置了SW_AGENT_NAME就使用系统变量的,否则值就是配置文件中定义的demo-provider

项目启动的时候在idea的VM中配置

-javaagent:/agent/skywalking-agent.jar -Dskywalking_config=./agent.config -Dskywalking.collector.backend_service=127.0.0.1:11800

javaagent是对应agent jar包的地址,skywalking_config是指定的配置文件,skywalking.collector.backend_service是Skywalking的oap服务端地址

源码分析

入口

SkyWalkingAgent的premain()是入口

public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException, IOException {
        final PluginFinder pluginFinder;
        try {
            SnifferConfigInitializer.initialize(agentArgs);

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

        } catch (ConfigNotFoundException ce) {
            logger.error(ce, "SkyWalking agent could not find config. Shutting down.");
            return;
        } catch (AgentPackageNotFoundException ape) {
            logger.error(ape, "Locate agent.jar failure. Shutting down.");
            return;
        } catch (Exception e) {
            logger.error(e, "SkyWalking agent initialized failure. Shutting down.");
            return;
        }

        final ByteBuddy byteBuddy = new ByteBuddy()
            .with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));

        AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy)
            .ignore(
                nameStartsWith("net.bytebuddy.")
                    .or(nameStartsWith("org.slf4j."))
                    .or(nameStartsWith("org.groovy."))
                    .or(nameContains("javassist"))
                    .or(nameContains(".asm."))
                    .or(nameContains(".reflectasm."))
                    .or(nameStartsWith("sun.reflect"))
                    .or(allSkyWalkingAgentExcludeToolkit())
                    .or(ElementMatchers.<TypeDescription>isSynthetic()));

        JDK9ModuleExporter.EdgeClasses edgeClasses = new JDK9ModuleExporter.EdgeClasses();
        try {
            agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses);
        } catch (Exception e) {
            logger.error(e, "SkyWalking agent inject bootstrap instrumentation failure. Shutting down.");
            return;
        }

        try {
            agentBuilder = JDK9ModuleExporter.openReadEdge(instrumentation, agentBuilder, edgeClasses);
        } catch (Exception e) {
            logger.error(e, "SkyWalking agent open read edge in JDK 9+ failure. Shutting down.");
            return;
        }

        agentBuilder
            .type(pluginFinder.buildMatch())
            .transform(new Transformer(pluginFinder))
            .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
            .with(new Listener())
            .installOn(instrumentation);

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

        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override public void run() {
                ServiceManager.INSTANCE.shutdown();
            }
        }, "skywalking service shutdown thread"));
    }
  1. 初始化配置信息,主要是Config类的属性填充:SnifferConfigInitializer.initialize(agentArgs),具体就是如果设置了指定的代理配置路径,就定位指定的代理配置。如果没有设置指定的代理配置路径,会定位config目录下的agent.config 文件,文件中的配置信息会保存到Properties 中,并通过PropertyPlaceholderHelper工具类转化为具体的值,利用ConfigInitializer工具类将对应value值添加到Config类中,包括SERVICE_NAME和内部类Collector中的BACKEND_SERVICE,我们之前说系统变量会覆盖配置文件中的配置,对应的就是overrideConfigBySystemProp()这个方法,方法中获取所有的环境变量,以skywalking开头的key都添加或覆盖到Config类中

  2. 加载插件文件,并分类保存到PluginFinder的成员变量中,对应方法为:pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());

    具体是使用AgentClassLoader类,AgentClassLoader的静态块中调用了tryRegisterAsParallelCapable()方法,这个方法主要解决jvm启动时的classloader死锁问题,只支持JDK7+,因为JDK7+出现了ParallelCapable并行加载,PluginResourcesResolver资源解析器的getResources()方法中使用AgentClassLoader的getResources()方法来加载 skywalking-plugin.def 文件,每个插件都会有 skywalking-plugin.def 文件,然后遍历所有插件的 skywalking-plugin.def 文件,解析内容保存到PluginDefine对象中,然后遍历PluginDefine对象,利用反射将插件类实例化,PluginFinder是插件查找器,它的构造方法中对AbstractClassEnhancePluginDefine集合进行遍历,根据AbstractClassEnhancePluginDefine的enhanceClass() 返回的 ClassMatcher 类型进行分类

  3. 使用ByteBuddy来创建AgentBuilder对象,具体来说先是创建ByteBuddy对象,用来生成动态类的,这和其他的动态代理差不多,目的就是对代理类进行增强拦截,然后创建AgentBuilder对象,忽略指定包包括net.bytebuddy开头的等等,通过PluginFInder.buildMatch() 方法对插件进行匹配,匹配后的类交给Transformer 进行处理,通过它的transform()方法进行增强

  4. 使用JDK的SPI加载的方式来启动插件服务,这部分内容我们将在下篇文章中介绍。

  5. 添加JVM钩子函数,这个构造函数中通过ServiceManager.INSTANCE.shutdown()来关闭所有的BootService服务。

总结

这篇文章对SKywalking在项目中如何引入使用进行介绍,我们了解到探针配置>JVM配置>系统环境变量配置>配置文件配置,并对Skywalking的源码进行分析,具体步骤是先初始化环境变量等信息,添加到Config类中,然后加载插件,每个插件都有skywalking-plugin.def 文件,读取内容保存到PluginDefine对象中,然后利用反射机制实例化插件,然后利用ByteBuddy动态创建对象,拦截增强,接着启动插件,最后添加关闭服务的钩子函数。

对于如何利用JDK的SPI机制来启动插件的,我们下篇文章介绍。

❤️ 感谢大家

如果你觉得这篇内容对你挺有有帮助的话:

  1. 欢迎关注我❤️,点赞👍🏻,评论🤤,转发🙏
  2. 关注盼盼小课堂,定期为你推送好文,还有群聊不定期抽奖活动,可以畅所欲言,与大神们一起交流,一起学习。
  3. 有不当之处欢迎批评指正。