Skywalking原理篇(一):Agent 启动流程解析

Java Agent简介

什么是Java Agent

Java Agent本质上可以理解为一个插件,该插件就是一个特制的Jar包。这个Jar包通过JVMTI(JVM Tool Interface)完成加载,最终借助JPLISAgent(Java Programming Language Instrumentation Services Agent)完成对目标代码的修改

如何实现一个Java Agent

实现Agent启动方法

Java Agent 支持目标JVM启动时加载以及JVM运行时加载,这两种不同的加载模式会使用不同的入口函数。
如果需要在目标JVM启动的同时加载 Agent,那么可以选择实现下面的方法

  1. public static void premain(String agentArgs, Instrumentation inst)
  2. public static void premain(String agentArgs)

如果需要在目标JVM运行时加载 Agent,那么可以选择实现下面的方法

  1. public static void agentmain(String agentArgs, Instrumentation inst)
  2. public static void agentmain(String agentArgs)

JVM 将首先寻找1,如果没有发现1,再寻找2。代码中有两个参数需要注意
agentArgs:-javaagent 命令携带的参数。agent.service_name 这个配置项的默认值有三种覆盖方式,其中,使用探针配置进行覆盖,探针配置的值就是通过该参数传入的
instjava.lang.instrumen.Instrumentation 是 Instrumention 包中定义的一个接口,它提供了操作类定义的相关方法,核心方法如下

  • addTransformer()/removeTransformer():注册/注销一个 ClassFileTransformer 类的实例,该 Transformer 会在类加载的时候被调用,可用于修改类定义
  • redefineClasses():该方法针对的是已经加载的类,它会对传入的类进行重新定义
  • getAllLoadedClasses():返回当前 JVM 已加载的所有类
  • getInitiatedClasses():返回当前 JVM 已经初始化的类
  • getObjectSize():获取参数指定的对象的大小

指定Main-Class

定义一个 MANIFEST.MF 文件,在其中添加 premain-classAgent-Class 配置项

Manifest-Version: 1.0
PreMain-Class: com.test.AgentClass
Agent-Class: com.test.AgentClass

Agent加载

  • 启动时加载:启动参数增加-javaagent:[path],其中path为对应的agent的jar包路径
  • 运行中加载:使用com.sun.tools.attach.VirtualMachine加载

SkyWalking Agent启动流程

核心流程

skywalking的agent入口函数在org.apache.skywalking.apm.agent.SkywalkingAgent#premain
方法具体实现如下(省略了异常代码块及日志打印)

final PluginFinder pluginFinder;
// 初始化配置信息
SnifferConfigInitializer.initializeCoreConfig(agentArgs);
// 加载agent插件, 并使用PluginFinder为插件进行分类
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
// 创建一个ByteBuddy对象用于修改字节码
final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
// 创建一个AgentBuilder对象,详细代码省略...
AgentBuilder agentBuilder = new AgentBuilder(byteBuddy)...
// 使用JDK SPI加载并启动BootService
ServiceManager.INSTANCE.boot();
// 添加一个JVM勾子函数, 在JVM退出时关闭所有BootService服务
Runtime.getRuntime()
    .addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread"));

核心启动流程如下图所示
在这里插入图片描述

初始化配置

配置初始化的入口方法在
org.apache.skywalking.apm.agent.core.conf.SnifferConfigInitializer
#initializeCoreConfig
方法的具体实现如下(省略了异常代码块及日志打印)

AGENT_SETTINGS = new Properties();
// 1.加载agent.config配置文件
final InputStreamReader configFileStream = loadConfig();
AGENT_SETTINGS.load(configFileStream);
for (String key : AGENT_SETTINGS.stringPropertyNames()) {
   
    String value = (String) AGENT_SETTINGS.get(key);
    // 2.解析agent.config文件中的配置
    AGENT_SETTINGS.put(key, PropertyPlaceholderHelper.INSTANCE.replacePlaceholders(value, AGENT_SETTINGS));
}
// 3.解析skywalking.开头的系统参数,截取后写入配置类
overrideConfigBySystemProp();
agentOptions = StringUtil.trim(agentOptions, ',');
if (!StringUtil.isEmpty(agentOptions)) {
   
	agentOptions = agentOptions.trim();
    // 4.解析Java Agent参数, 写入配置类
    overrideConfigByAgentOptions(agentOptions);
}
// 5.将配置类中的信息填充到Config类对应的静态字段中
initializeConfig(Config.class);
IS_INIT_COMPLETED = true;

可以看到配置信息会依次从三个地方进行获取并覆盖

  • agent.config 文件
  • 系统参数
  • javaagent options

所以,配置参数的优先级为 javaagent options > 系统参数 > agent.config文件
下面通过在3个地方同时配置 agent.service_name 参数来验证一下配置加载流程
Live-Demo + skywalking源码 项目为例
修改项目 ProjectB 的JVM启动参数,同时配置 javaagent options、系统参数

-javaagent:/Users/wangbo/Documents/Study_Workspace/skywalking/skywalking-agent/skywalking-agent.jar=agent.service_name=Project_Options
-Dskywalking.agent.service_name=Project_Env

覆盖 agent.config 中指定参数的的环境变量

SW_AGENT_NAME=Project_Config

在这里插入图片描述

然后debug启动 projectB 进行源码调试

加载agent.config配置文件

private static final String SPECIFIED_CONFIG_PATH = "skywalking_config";
private static final String DEFAULT_CONFIG_FILE_NAME = "/config/agent.config";
String specifiedConfigPath = System.getProperty(SPECIFIED_CONFIG_PATH);
File configFile = StringUtil.isEmpty(specifiedConfigPath) ? new File(
    AgentPackagePath.getPath(), DEFAULT_CONFIG_FILE_NAME) : new File(specifiedConfigPath);

源码中第一步为加载 agent.config 配置文件,可以看出agent.config 文件加载的优先级为
环境变量 skywalking_config 指定的路径 > skywalking-agent.jar 同级的config目录

解析agent.config配置

在这里插入图片描述
第一步agent.config文件加载到properties后,默认的配置格式都是 **配置项 = ${环境变量:配置默认值}**
第二步将对具体的值进行解析,首先判断环境变量中是否包含该配置,有则替换,无则使用默认值,所以解析后的配置格式变为 **配置项 = 配置值** 。可以看到 agent.service_name 对应的值为Project_Config

解析系统参数配置在这里插入图片描述

第三步为解析系统参数,可以看到 agent.service_name 被覆盖为 Project_Env

解析javaagent options配置

在这里插入图片描述
第四步为解析javaagent options参数,可以看到agent.service_name被覆盖为Project_Options

将解析出来的配置全部填充到Conig静态配置类中

在这里插入图片描述
配置加载的最终目的就是将配置项全部填充到Config配置类的各个静态字段中,这样后续使用配置信息时直接通过Config类进行获取即可,可以看到之前配置的 agent.service_name 在最后一步被确定为 Project_Options

插件加载

加载插件的入口方法在
org.apache.skywalking.apm.agent.core.plugin.PluginBootstrap#loadPlugins
方法的具体实现如下

// 自定义类加载器初始化
AgentClassLoader.initDefaultLoader();
// 创建插件定义解析类
PluginResourcesResolver resolver = new PluginResourcesResolver();
// 通过调用AgentClassLoader的资源加载方法解析出所有插件定义文件的路径
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值