概述
SkyWalking 分为两个部分,一部分是探针用于收集指标,另一部分是服务端用于接收探针发来的指标进行展示。
其中SkyWalking 的探针是基于 JavaAgent
机制。可以通过以下两篇文章熟悉 JavaAgent
并且通过 ByteBuddy
修改 Java 类的二进制,通过以下文章熟悉 ByteBuddy
- 《Easily Create Java Agents with Byte Buddy》
- 《Java字节码3-使用ByteBuddy实现一个Java-Agent》
- 《Byte Buddy 教程》
- 《skywalking源码分析之javaAgent工具ByteBuddy的应用》
源码分析
public class SkyWalkingAgent {
private static final ILog logger = LogManager.getLogger(SkyWalkingAgent.class);
/**
* Main entrance. Use byte-buddy transform to enhance all classes, which define in plugins.
*
* @param agentArgs
* @param instrumentation
* @throws PluginException
*/
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
final PluginFinder pluginFinder;
try {
//① 初始化配置信息 该步骤中会加载 agent.config 配置文件,其中会检测 Java Agent 参数以及环境变量是否覆盖了相应配置项。
SnifferConfigInitializer.initialize(agentArgs);
//② 查找并解析skywalking-plugin.def 插件文件;
// AgentClassLoader 加载插件类并进行实例化;PluginFinder 提供插件匹配的功能
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;
}
//③ 使用Byte Buddy 库创建 AgentBuilder
final ByteBuddy byteBuddy = new ByteBuddy()
.with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
//④ 开始创建 AgentBuilder , AgentBuilder 是 Byte Buddy 库专门用来支持 Java Agent 的一个 API
new AgentBuilder.Default(byteBuddy) // 设置使用的ByteBuddy对象
.ignore( //忽略指定包中的类,对这些类不会进行拦截增强。
nameStartsWith("net.bytebuddy.") // 不会拦截下列包中的类
.or(nameStartsWith("org.slf4j."))
.or(nameStartsWith("org.apache.logging."))
.or(nameStartsWith("org.groovy."))
.or(nameContains("javassist"))
.or(nameContains(".asm."))
.or(nameStartsWith("sun.reflect"))
.or(allSkyWalkingAgentExcludeToolkit()) // 处理 Skywalking 的类
// synthetic类和方法是由编译器生成的,这种类也需要忽略
.or(ElementMatchers.<TypeDescription>isSynthetic()))
.type(pluginFinder.buildMatch())// 拦截 在类加载时根据传入的 ElementMatcher 进行拦截,拦截到的目标类将会被 transform() 方法中指定的 Transformer 进行增强。
.transform(new Transformer(pluginFinder)) // 设置Transform 这里指定的 Transformer 会对前面拦截到的类进行增强。
.with(new Listener()) // 设置Listener 添加一个 Listener 用来监听 AgentBuilder 触发的事件。
.installOn(instrumentation);
try {
//⑤ 使用jdk spi 加载的方式并启动 BootService 服务
ServiceManager.INSTANCE.boot();
} catch (Exception e) {
logger.error(e, "Skywalking agent boot failure.");
}
//⑥ 添加一个JVM 勾子
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override public void run() {
ServiceManager.INSTANCE.shutdown();
}
}, "skywalking service shutdown thread"));
}
//内部类
private static class Transformer implements AgentBuilder.Transformer {
private PluginFinder pluginFinder;
Transformer(PluginFinder pluginFinder) {
this.pluginFinder = pluginFinder;
}
//插件增强目标类的入口
//Skywalking.Transformer 会通过 PluginFinder 查找目标类匹配的插件(即 AbstractClassEnhancePluginDefine 对象),
//然后交由 AbstractClassEnhancePluginDefine 完成增强
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
TypeDescription typeDescription,// 被拦截的目标类
ClassLoader classLoader, //加载目标类的ClassLoader
JavaModule module) {
// 从PluginFinder中查找匹配该目标类的插件
List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
if (pluginDefines.size() > 0) {
DynamicType.Builder<?> newBuilder = builder;
EnhanceContext context = new EnhanceContext();
for (AbstractClassEnhancePluginDefine define : pluginDefines) {
// AbstractClassEnhancePluginDefine.define()方法是插件入口,
// 在其中完成了对目标类的增强
DynamicType.Builder<?> possibleNewBuilder = define.define(typeDescription, newBuilder, classLoader, context);
if (possibleNewBuilder != null) {
// 注意这里,如果匹配了多个插件,会被增强多次
newBuilder = possibleNewBuilder;
}
}
if (context.isEnhanced()) {
logger.debug("Finish the prepare stage for {}.", typeDescription.getName());
}
return newBuilder;
}
logger.debug("Matched class {}, but ignore by finding mechanism.", typeDescription.getTypeName());
return builder;
}
}
private static ElementMatcher.Junction<NamedElement> allSkyWalkingAgentExcludeToolkit() {
return nameStartsWith("org.apache.skywalking.").and(not(nameStartsWith("org.apache.skywalking.apm.toolkit.")));
}
private static class Listener implements AgentBuilder.Listener {
@Override
public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
}
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module,
boolean loaded, DynamicType dynamicType) {
if (logger.isDebugEnable()) {
logger.debug("On Transformation class {}.", typeDescription.getName());
}
InstrumentDebuggingClass.INSTANCE.log(typeDescription, dynamicType);
}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module,
boolean loaded) {
}
@Override
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded,
Throwable throwable) {
logger.error("Enhance class " + typeName + " error.", throwable);
}
@Override
public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
}
}
}
- ① 初始化配置信息 该步骤中会加载
agent.config
配置文件,其中会检测 Java Agent 参数以及环境变量是否覆盖了相应配置项。 - ② 查找并解析
skywalking-plugin.def
插件文件;AgentClassLoader 加载插件类并进行实例化;PluginFinder 提供插件匹配的功能 - ③ 使用Byte Buddy 库创建
AgentBuilder
- ④ 开始创建 AgentBuilder , AgentBuilder 是
Byte Buddy
库专门用来支持 Java Agent 的一个 API - ⑤ 使用jdk spi 加载的方式并启动
BootService
服务 - ⑥ 添加一个JVM 勾子
接下来的文章会一一分析每一步的实现。