文章目录
原理图
启动流程
-
SnifferConfigInitializer.initialize负责基于agent.config配置文件解析Config
-
PluginBootstrap.loadPlugin基于PluginBootstrap插件引导程序加载插件[AbstractClassEnhancePluginDefine]集合
-
通过PluginFinder将插件分类
-
基于IS_OPEN_DEBUGGING_CLASS配置将被增强类写至磁盘
-
忽略对特定包的增强[如javassist,net.bytebuddy.等等]
-
基于PluginFinder对字节码进行增强,并装载至instrumentation
-
启动agent端基于SPI的所有BootService
BootService默认启动集合
TraceSegmentServiceClient
ContextManager
SamplingService
GRPCChannelManager
JVMService
ServiceAndEndpointRegisterClient
ContextManagerExtendService
CommandService
CommandExecutorService
OperationNameFormatService
PluginBootstrap.loadPlugins加载插件流程:
- 构建AgentClassLoader(指定加载目录plugins和activations)
- 通过PluginResourcesResolver加载所有的skywalking-plugin.def文件
- 通过PluginCfg解析skywalking-plugin.def文件中定义的插件并转成PluginDefine集合
- Class.forName将所有PluginDefine定义的插件实例化为AbstractClassEnhancePluginDefine
PluginFinder实例化流程
- 将插件加载器构建的插件集合分类
- nameMatchDefine: 存入所有按插件名称匹配的插件
- signatureMatchDefine: 存入nameMatchDefine之外的其他插件[注解,签名]等
- bootstrapClassMatchDefine: 存入bootstrap-plugins包目录下定义的插件
源码分析一SkyWalkingAgent.premain
- 通过agent.config文件初始化配置Config类
- 加载plugins和activations目录下所有skywalking-plugin.def定义的插件
- 通过Transformer定义字节码拦截和增强逻辑,并安装至instrumentation,使字节码增强逻辑生效
- 通过SPI启动所有BootService
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException, IOException {
final PluginFinder pluginFinder;
...... 删除其他代码
${AGENT_PACKAGE_PATH}/config/agent.config 初始化配置
SnifferConfigInitializer.initialize(agentArgs);
加载插件 [根据名称]分类注入 finder skywalking-plugin.def PluginBootstrap插件引导程序类
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
...... 删除其他代码
基于配置存储被代理增强类类到debug目录
final ByteBuddy byteBuddy = new ByteBuddy()
.with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
构建 AgentBuilder 配置ElementMatcher先忽略对指定目录的字节码插桩
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()));
...... 删除jdk9相关
字节码注入 每当一个类进行加载 需要代理建造者判断type是否合适,合适就通过Transformer增强
agentBuilder
找到元素匹配ElementMatchers需要拦截的类对应的插件拦截器 [将所有插件的匹配规则都搂进来]
.type(pluginFinder.buildMatch())
代理插件的增强 设置 Java 类的修改逻辑 定义transformer,每有一个类加载时,触发transformer逻辑,对类进行匹配和修改。
.transform(new Transformer(pluginFinder))
redefinition策略,描述agent如何控制已经被agent 加载到内存里面的类 采用了重新定义 这里读者自行了解下类型重定义与类型重定基底(等bytebuddy知识)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
Java 类的修改情况回调
.with(new Listener())
.installOn(instrumentation);
启动 grpc jvm监控等顶级组件
ServiceManager.INSTANCE.boot();
...... 删除其他代码
注册关闭挂钩
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override public void run() {
ServiceManager.INSTANCE.shutdown();
}
}, "skywalking service shutdown thread"));
}
源码分析一PluginBootstrap.loadPlugins
- 实例化加载plugins和activations目录类加载器AgentClassLoader
- 通过skywalking-plugin.def解析获取所有的插件定义PluginDefine
- 通过插件定义集合实例化所有的插件集合[AbstractClassEnhancePluginDefine]
public List<AbstractClassEnhancePluginDefine> loadPlugins() throws AgentPackageNotFoundException {
PluginBootstrap对应的skywalking类加载器,加载plugins和activations目录
AgentClassLoader.initDefaultLoader();
PluginResourcesResolver resolver = new PluginResourcesResolver();
获得skywalking-plugin.def插件定义路径数组
List<URL> resources = resolver.getResources();
...... 删除其他代码
获得插件定义类( org.skywalking.apm.agent.core.plugin.PluginDefine )数组。
for (URL pluginUrl : resources) {
try {
PluginCfg.INSTANCE.load(pluginUrl.openStream());
} catch (Throwable t) {
logger.error(t, "plugin file [{}] init failure.", pluginUrl);
}
}
List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();
创建类增强插件定义(AbstractClassEnhancePluginDefine )对象数组并通过class.forName添加插件
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());
}
}
所有增强类的插件加载到这里 类似于aop类已经注入容器 这里所有插件即将放入finder
plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));
return plugins;
}
源码分析一PluginFinder实例化
- 将获取到的插件集合分成三类
- 按类名称匹配的插件nameMatchDefine
- 按签名插件匹配的插件signatureMatchDefine[ 如注解 继承]
- 引导类插件bootstrapClassMatchDefine[ bootstrap-plugins包目录下的插件]
public PluginFinder(List<AbstractClassEnhancePluginDefine> plugins) {
for (AbstractClassEnhancePluginDefine plugin : plugins) {
ClassMatch match = plugin.enhanceClass();
名称插件
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);
}
引导类插件: bootstrap-plugins包目录下的插件
if (plugin.isBootstrapInstrumentation()) {
bootstrapClassMatchDefine.add(plugin);
}
}
}
源码分析一Transformer增强逻辑
- 当源目标类被加载时通过Transformer完成增强
- 找出所有匹配目标类的插件AbstractClassEnhancePluginDefine
- 通过插件AbstractClassEnhancePluginDefine.define完成类增强逻辑
private static class Transformer implements AgentBuilder.Transformer {
private PluginFinder pluginFinder;
Transformer(PluginFinder pluginFinder) {
this.pluginFinder = pluginFinder;
}
/**
*
* @param builder 字节码探针处理器
* @param typeDescription加载时候对应的源类的元信息
* @param classLoader
* @param module 这个是java9的特性
* @return
*/
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
所有适合该类的插件[ElementMatchers.match]
List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
if (pluginDefines.size() > 0) {
DynamicType.Builder<?> newBuilder = builder;
增强上下文
EnhanceContext context = new EnhanceContext();
for (AbstractClassEnhancePluginDefine define : pluginDefines) {
通过DynamicType.Builder对象,定义如何拦截需要修改的Java类
bytebuddy的动态agent逻辑
DynamicType.Builder<?> possibleNewBuilder = define.define(typeDescription, newBuilder, classLoader, context);
在增强的基础上在增强,后一个插件是在前一个已经修改的字节码上继续修改
if (possibleNewBuilder != null) {
newBuilder = possibleNewBuilder;
}
}
return newBuilder;
}
return builder;
}
}
总结
- agent端同服务端采用微内核架构
- 通过扫描所有的skywalking-plugin.def完成插件的载入和字节码增强功能
- 借助jdk提供的javaagent功能实现字节码插桩
扩展点一ClassMatch匹配机制
类名称 | 作用 |
---|---|
NameMatch | 基于完整的类名进行匹配 |
IndirectMatch | 间接匹配接口 |
ClassAnnotationMatch | 基于类注解进行匹配,可设置同时匹配多个 |
HierarchyMatch | 基于父类 / 接口进行匹配,可设置同时匹配多个 |
MethodAnnotationMatch | 基于方法注解进行匹配,可设置同时匹配多个 |
扩展点一Config配置类
- 多个配置子类,基于agent.config初始化该类
- 可以对Agent,Jvm,日志,Plugin等功能增加特性配置
扩展点一BootService一览
名称 | 作用 |
---|---|
TraceSegmentServiceClient | 核心功能:trace上报 |
ContextManager | TraceContext核心处理入口,负责span,Trace的创建,传播 |
SamplingService | 全采样还是采样阈值控制服务 |
GRPCChannelManager | 基于BACKEND_SERVICE配置管理OapServer的GrpcChannel |
JVMService | 核心功能:Metrics指标上报 |
ServiceAndEndpointRegisterClient | 核心服务注册信息同步以及Dictionary字典维护 |
ContextManagerExtendService | 工具类:TracerContext创建工具 |
CommandService | 命令管理服务 |
CommandExecutorService | 消费命令管理服务存在的命令 |
OperationNameFormatService | 端点格式化工具 |