Skywalking8.9.1源码解析<八>-Skywalking-Agent启动流程
- 写在开篇:本系列文章中,有自己的体会,有网上借鉴,如有雷同,请联系本人删除,莫要举报,本人无收益,纯属记录。若再次被封禁,将不再解封。
概览
Skywalking的原理是java-agent,所以整个核心的启动方法也就是premain方法,主要执行流程如下
代码解析:
/**
* Main entrance. Use byte-buddy transform to enhance all classes, which define in plugins.
* agentArgs: -javaagent:/path/to/agent.jar=agentArgs,配置参数后得参数
* Instrumentation:插庄服务的接口,在计算机科学技术中的英文释义是插桩、植入
* 开发者可以构建一个独立于应用程序的代理程序(Agent),用来检测和协助运行在JVM上的程序,甚至可以替换和修改某些类上的定义,
* 这样的特性实际上提供了一种虚拟机级别的支持AOP的方式,是的开发者对原有应用不做任何改变,就可以实现动态修改和增强
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
// 这里面的每一行代码都会在,一个服务运行的时候重新运行一次
final PluginFinder pluginFinder;
try {
// 1、初始化配置信息,加载agent.config配置文件,其中会检测Java Agent参数以及环境变量是否覆盖了相应配置
SnifferConfigInitializer.initializeCoreConfig(agentArgs);
} catch (Exception e) {
// 这里需要重新加载日志配置
LogManager.getLogger(SkyWalkingAgent.class)
.error(e, "SkyWalking agent initialized failure. Shutting down.");
return;
} finally {
// refresh logger again after initialization finishes
LOGGER = LogManager.getLogger(SkyWalkingAgent.class);
try {
// 2、AgentClassLoader 加载插件类并进行实例化: PluginFinder提供插件匹配功能,使用自定义类加载器加载插件,这样就可以让应用无依赖无感知的插件,加载所有插件
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
} 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;
// 3、使用ByteBuddy 创建AgentBuilder,使用ByteBuddy来进行字节码增强
final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
// 指定bytebuddy忽略的类
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.isSynthetic())); // java编译器编译的时候生成的一些类,这些类找不到源代码
JDK9ModuleExporter.EdgeClasses edgeClasses = new JDK9ModuleExporter.EdgeClasses();
try {
// 注入到BootStrapClassLoader
agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses);
} catch (Exception e) {
LOGGER.error(e, "SkyWalking agent inject bootstrap instrumentation failure. Shutting down.");
return;
try {
//解决JDK9模块问题
agentBuilder = JDK9ModuleExporter.openReadEdge(instrumentation, agentBuilder, edgeClasses);
} catch (Exception e) {
LOGGER.error(e, "SkyWalking agent open read edge in JDK 9+ failure. Shutting down.");
return;
// 是否把修改的字节码保存一份。默认不加
if (Config.Agent.IS_CACHE_ENHANCED_CLASS) {
try {
agentBuilder = agentBuilder.with(new CacheableTransformerDecorator(Config.Agent.CLASS_CACHE_MODE));
LOGGER.info("SkyWalking agent class cache [{}] activated.", Config.Agent.CLASS_CACHE_MODE);
} catch (Exception e) {
LOGGER.error(e, "SkyWalking agent can't active class cache.");
// 指定ByteBuddy要拦截的类
agentBuilder.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))// 插庄,字节码增强,改字节码
// 重定义与retransform的区别就是是否保留之前存在的内容
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new RedefinitionListener())
.with(new Listener())
.installOn(instrumentation);
try {
// 4、采用Spi加载方式并启动BootService服务
ServiceManager.INSTANCE.boot();
} catch (Exception e) {
LOGGER.error(e, "Skywalking agent boot failure.");
// 5、添加JVM钩子
Runtime.getRuntime()
.addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread"));
1、初始化配置
入口:#SnifferConfigInitializer.initializeCoreConfig(agentArgs);
作用:初始化配置信息,如下图所示
- 加载配置信息
AGENT_SETTINGS = new Properties();
// 通过loadConfig()方法加载配置信息,优先级最低
try (final InputStreamReader configFileStream = loadConfig()) {
AGENT_SETTINGS.load(configFileStream);
for (String key : AGENT_SETTINGS.stringPropertyNames()) {
String value = (String) AGENT_SETTINGS.get(key);
AGENT_SETTINGS.put(key, PropertyPlaceholderHelper.INSTANCE.replacePlaceholders(value, AGENT_SETTINGS));
} catch (Exception e) {
LOGGER.error(e, "Failed to read the config file, skywalking is going to run in default config.");
try {
// 用系统环境变量参数覆盖上述配置文件中的配置,优先级第二
overrideConfigBySystemProp();
} catch (Exception e) {
LOGGER.error(e, "Failed to read the system properties.");
agentOptions = StringUtil.trim(agentOptions, ',');
if (!StringUtil.isEmpty(agentOptions)) {
try {
agentOptions = agentOptions.trim();
LOGGER.info("Agent options is {}.", agentOptions);
//-javaagent:/path/to/agent.jar=agentArgs,中的参数,优先级最高,会覆盖上面配置
overrideConfigByAgentOptions(agentOptions);
} catch (Exception e) {
LOGGER.error(e, "Failed to parse the agent options, val is {}.", agentOptions);
- 将配置信息映射到org.apache.skywalking.apm.agent.core.conf#Config类中的属性 入口:#initializeConfig(Config.class);
public static void initializeConfig(Class configClass) {
if (AGENT_SETTINGS == null) {
LOGGER.error("Plugin configs have to be initialized after core config initialization.");
return;
try {
// 会将最终的配置信息填充到Config的静态字段中,将agent.config文件中全部配置信息填充到config中
ConfigInitializer.initialize(AGENT_SETTINGS, configClass);
} catch (IllegalAccessException e) {
LOGGER.error(e,
"Failed to set the agent settings {}"
+ " to Config={} ",
AGENT_SETTINGS, configClass
- 初始化日志,根据配置重新选择指定日志解释器(如果基于Skywalking进行开发,不会用到它们的日志,所以这块可以裁剪)
- 检测探针名称、服务器地址信息是否配置
if (StringUtil.isEmpty(Config.Agent.SERVICE_NAME)) {
throw new ExceptionInInitializerError("`agent.service_name` is missing.");
if (StringUtil.isEmpty(Config.Collector.BACKEND_SERVICE)) {
throw new Excep
//标记完成配置初始化
IS_INIT_COMPLETED = true;
2、加载插件
方法入口:#org.apache.skywalking.apm.agent.core.plugin.PluginBootstrap#loadPlugins()
- 初始化自定义类加载器
// 初始化自定义加载器,该方法中会选择合适的类加载器,并示例话AgentClassLoader加载指定路径下的插件
AgentClassLoader.initDefaultLoader();
初始化方法入口:#org.apache.skywalking.apm.agent.core.plugin.loader.AgnetClassLoader 其中静态代码块中调用了registerAsParallelCapable()方法,开启并行类加载功能。
旧版的 AgentClassLoader 未开启并行机制,默认的loadClass方法会使用 synchronized 锁住当前的类加载器对象,当 WebAppClassLoader 与 AgentClassLoader 加载的类有依赖关系时就会产生死锁。而开启了并行机制后,loadClass 会将锁的级别降低到被加载的类的级别上,不再对类加载器进行加锁
static {
* Try to solve the classloader dead lock. See https://github.com/apache/skywalking/pull/2016
* 为什么要用类加载器?之所以自定义类加载器,是因为应用默认的类加载器只能加载JVM相关的类库及Classpath下的类库,Skywalking为了实现不修改应用结构,无侵入加载jar包,不在应用的classpath中引入Skywalking的插件jar包。这样就可以让应用无依赖无感知插件
* 自定义类加载器,负责查找插件和拦截器,为了解决ClassLoader死锁问题,开启类加载器的并行加载模式
* JDK1.7之前,类加载器在加载类的时候是串行加载的,比如100个类需要加载,那么就排队,加载完上一个在加载下一个,这样加载效率低
* JDK1.7之后,就提供了类的并行能力,就是把锁的粒度变小,之前ClassLoader加载类的时候,加锁的时候使用自身作为锁的
* 当委派模型没有严格分层的环境中(出现闭环委托),这个时候就需要类加载器具有并行能力,否则会导致死锁
registerAsParallelCapable();
- 插件加载 加载指定路径下的插件:
public AgentClassLoader(ClassLoader parent) throws AgentPackageNotFoundException {
super(parent);
// 获取探针的目录
File agentDictionary = AgentPackagePath.getPath();
classpath = new LinkedList<>();
// 默认agent目录下的plugin和activations目录作为AgentClassLoader的classpath,只有这两个目录下的jar包才会被AgentClassLoader所加载
Config.Plugin.MOUNT.forEach(mountFolder -> classpath.add(new File(agentDictionary, mountFolder)));
解析插件#org.apache.skywalking.apm.agent.core.plugin.PluginBootstrap#loadPlugins()
PluginResourcesResolver resolver = new PluginResourcesResolver();
// 拿到所有skywalking-plugin.def资源
List<URL> resources = resolver.getResources();
//利用用PluginCfg类将def中的定义转换位PluginDefine对象
for (URL pluginUrl : resources) {
try {
// 方法会把skywalking-plugin.def转换为PluginDefine对象(属性:插件名和插件定义类名 )
PluginCfg.INSTANCE.load(pluginUrl.openStream());
} catch (Throwable t) {
LOGGER.error(t, "plugin file [{}] init failure.", pluginUrl);
// 然后遍历所有 `PluginDefine` 对象,通过反射 `defineClass` 字段将每一个插件实例化为\`AbstractClassEnhancePluginDefine` 对象(为什么转换为这个对象,参考官方插件开发指南),添加到plugins列表中,
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());
//最后将SPI发现并实例化其他插件也添加到plugins列表中返回
plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));
插件定义#org.apache.skywalking.apm.agent.core.plugin.PluginFinder#PluginFinder()
public PluginFinder(List<AbstractClassEnhancePluginDefine> plugins) {
for (AbstractClassEnhancePluginDefine plugin : plugins) {
// enClass()方法,根据返回的ClassMatch类型进行分类,然后写入到不同的集合中,并在后面的ByteBuddy匹配需要增强类时传入匹配规则
ClassMatch match = plugin.enhanceClass();
if (match == null) {
continue;
// 适合用NameMatch的插件列表,使用完整类名进行匹配
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 {
// 使用InderectMatch的插件列表,基于类注解、父类、接口、方法注解进行的匹配
signatureMatchDefine.add(plugin);
// jdk内置,引导类插件列表
if (plugin.isBootstrapInstrumentation()) {
bootstrapClassMatchDefine.add(plugin);
4、字节码增强
- 创建AgentBuilder,AgentBulder是专门用来支持java agent的一个API,在premian()方法中的实现如下:
// 使用ByteBuddy 创建AgentBuilder,使用ByteBuddy来进行字节码增强
final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
// 指定bytebuddy忽略的类
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.isSynthetic())); // java编译器编译的时候生成的一些类,这些类找不到源代码
// 指定ByteBuddy要拦截的类
agentBuilder.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))// 插庄,字节码增强,改字节码,具体的增强逻辑
// 重定义与retransform的区别就是是否保留之前存在的内容
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new RedefinitionListener())
// 回调接口,用来在Debug模式下保存增强后的字节码文件
.with(new Listener())
.installOn(instrumentation);
增强类匹配pluginFinder.buildMatch()
// 只有符合下述条件的类,才能被ByteBudy字节码增强
public ElementMatcher<? super TypeDescription> buildMatch() {
// 建立匹配规则,在内部类中添加第一个默认规则
ElementMatcher.Junction judge = new AbstractJunction<NamedElement>() {
@Override
public boolean matches(NamedElement target) {
// 按名称匹配
return nameMatchDefine.containsKey(target.getActualName());
// 不能是接口,因为接口没有字节码实现
judge = judge.and(not(isInterface()));
// 遍历所有特殊匹配模式的插件列表
for (AbstractClassEnhancePluginDefine define : signatureMatchDefine) {
ClassMatch match = define.enhanceClass();
if (match instanceof IndirectMatch) {
//获取特殊匹配规则
judge = judge.or(((IndirectMatch) match).buildJunction());
// 返回ByteBuddy元素匹配器
return new ProtectiveShieldMatcher(judge);
public List<AbstractClassEnhancePluginDefine> getBootstrapClassMatchDefine() {
return bootstrapClassMatchDefine;
创建增强逻辑,主要在AgentBuilder中的transfor()方法中,其中的new Transformer()就是触发增强逻辑的主要入口
private static class Transformer implements AgentBuilder.Transformer {
private PluginFinder pluginFinder;
Transformer(PluginFinder pluginFinder) {
this.pluginFinder = pluginFinder;
// 打桩
@Override
public DynamicType.Builder<?> transform(final DynamicType.Builder<?> builder, // 当前拦截到类的字节码
final TypeDescription typeDescription, // 简单当成Class, 它包含了类的描述信息
final ClassLoader classLoader, // 加载当前拦截到类的类加载器
final JavaModule module) {
// 注册URL类加载器,它用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。也就是说,通过URLClassLoader就可以加载指定jar中的class到内存中。
LoadedLibraryCollector.registerURLClassLoader(classLoader);
List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
if (pluginDefines.size() > 0) {
DynamicType.Builder<?> newBuilder = builder;
// EnhanceContext是为了保证流程的一个记录器,比如执行了某个步骤之后就会记录下来,防止重复操作
EnhanceContext context = new EnhanceContext();
for (AbstractClassEnhancePluginDefine define : pluginDefines) {
// 调用插件定义的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;
5、BootService启动
根据官方插件开发指南得知,所有插件服务都要实现接口org.apache.skywalking.apm.agent.core.boot.BootService
/**
* The <code>BootService</code> is an interface to all remote, which need to boot when plugin mechanism begins to work.
* {@link #boot()} will be called when <code>BootService</code> start up.
* 定义了一个服务的生命周期
public interface BootService {
void prepare() throws Throwable;
void boot() throws Throwable;
void onComplete() throws Throwable;
void shutdown() throws Throwable;
* {@code BootService}s with higher priorities will be started earlier, and shut down later than those {@code BootService}s with lower priorities.
* 越大越先启动
* @return the priority of this {@code BootService}.
default int priority() {
return 0;
除此之外,每个服务实现还需要使用两个注解
@DefaultImplementor 用于标识BootService接口 @OverrideImplmentoe用于覆盖默认BootService实现,通过其value关键字指定要覆盖的默认实现,覆盖实现只能覆盖默认实现
采用Spi加载方式并启动BootService服务, ServiceManager.INSTANCE.boot();
// BootService实例容器,使用map结构方便服务间直接通过Class对象进行调用
private Map<Class, BootService> bootedServices = Collections.emptyMap();
public void boot() {
// 加载所有BootService服务的实现,并写入实例容器
bootedServices = loadAllServices();
// 循环调用所有服务的prepare方法
prepare();
// 循环调用所有服务的boot方法
startup();
// 循环调用所有服务的onComplete方法
onComplete();