SkyWalking Agent 启动流程概述
SkyWalking Agent 的入口是 apm-agent 模块中 SkyWalkingAgent 类的 premain() 方法,其中完成了 Agent 启动的流程:
- 初始化配置信息。该步骤中会加载 agent.config 配置文件,其中会检测 Java Agent 参数以及环境变量是否覆盖了相应配置项。
- PluginFinder 对插件进行分类管理。
- 使用 Byte Buddy 库创建 AgentBuilder。这里会根据已加载的插件动态增强目标类,插入埋点逻辑。
- 使用 JDK SPI 加载并启动 BootService 服务。BootService 接口的实现会在后面的课时中展开详细介绍。
- 添加一个 JVM 钩子,在 JVM 退出时关闭所有 BootService 服务。
SkywalkingAgent.premain() 方法的具体实现如下,其中省略了 try/catch 代码块以及异常处理逻辑:
/**
* Main entrance. Use byte-buddy transform to enhance all classes, which define in plugins.
*/
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
final PluginFinder pluginFinder;
try {
// 步骤1、初始化配置信息
SnifferConfigInitializer.initializeCoreConfig(agentArgs);
// 加载插件 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;
}
// 使用 Byte Buddy 库创建 AgentBuilder
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.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;
}
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.");
}
}
agentBuilder.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new Listener())
.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(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread"));
}
初始化配置信息
如果指定了agent config路径,程序会专门加载指定路径。如果没有配置agent config路径,则程序会加载config文件下的agent.config文件。同时,也可以使用系统属性覆盖配置。所有的配置的key必须以skywalking.开头,例如使用_skywalking.agent.service_name=yourAppName来覆盖配置文件中的 'agent.service_name'。
另外,在所有的配置信息中,agent.service_name and collector.servers这两个配置的值都是不能为空的。
_
在 SnifferConfigInitializer.initialize() 方法中会将最终的配置信息填充到 Config 的静态字段中,填充过程如下:
- 将 agent.config 文件中全部配置信息填充到 Config 中相应的静态字段中。
- 解析系统环境变量值,覆盖 Config 中相应的静态字段。
- 解析 Java Agent 的参数,覆盖 Config 中相应的静态字段。
SnifferConfigInitializer.initialize() 方法的具体实现如下:
/**
* If the specified agent config path is set, the agent will try to locate the specified agent config. If the
* specified agent config path is not set , the agent will try to locate `agent.config`, which should be in the
* /config directory of agent package.
* <p>
* Also try to override the config by system.properties. All the keys in this place should start with {@link
* #ENV_KEY_PREFIX}. e.g. in env `skywalking.agent.service_name=yourAppName` to override `agent.service_name` in
* config file.
* <p>
* At the end, `agent.service_name` and `collector.servers` must not be blank.
*/
public static void initializeCoreConfig(String agentOptions) {
AGENT_SETTINGS = new Properties();
// 加载 agent.config配置文件
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 {
// 解析环境变量,并覆盖 Config中相应的静态字段
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);
// 解析 Java Agent参数,并覆盖 Config中相应的静态字段
overrideConfigByAgentOptions(agentOptions);
} catch (Exception e) {
logger.error(e, "Failed to parse the agent options, val is {}.", agentOptions);
}
}
initializeConfig(Config.class);
// 检测SERVICE_NAME和BACKEND_SERVICE两个配置项,若为空则抛异常(略)
if (StringUtil.isEmpty(Config.Agent.SERVICE_NAME)) {
throw new ExceptionInInitializerError("`agent.service_name` is missing.");
}
if (StringUtil.isEmpty(Config.Collector.BACKEND_SERVICE)) {
throw new ExceptionInInitializerError("`collector.backend_service` is missing.");
}
if (Config.Plugin.PEER_MAX_LENGTH <= 3) {
logger.warn(
"PEER_MAX_LENGTH configuration:{} error, the default value of 200 will be used.",
Config.Plugin.PEER_MAX_LENGTH
);
Config.Plugin.PEER_MAX_LENGTH = 200;
}
// 更新初始化标记
IS_INIT_COMPLETED = true;
}
在loadConfig() 方法中,会优先根据环境变量(skywalking_config)指定的 agent.config 文件路径加载。若环境变量未指定 skywalking_ config 配置,则到 skywalking-agent.jar 同级的 config 目录下查找 agent.confg 配置文件。
将 agent.config 文件中的配置信息加载到 Properties 对象之后,此时配置值还是带有占位符号的值。需要使用 PropertyPlaceholderHelper 对配置信息进行解析,将当前的“${配置项名称:默认值}”格式的配置值,并替换配置中的默认值。
在接下来的 overrideConfigBySystemProp() 方法中会遍历环境变量(即 System.getProperties() 集合),如果环境变 是以 "skywalking." 开头的,则认为是 SkyWalking 的配置,会填充到 Properties属性中,以覆盖 agent.config 中的默认值。
最后的 overrideConfigByAgentOptions() 方法解析的是 Java Agent 的参数。它也是将配置信息转换成Property。如果与上述的配置文件,系统属性不一样,则进行覆盖。
在生成完成Property后,会将配置信息填充到 Config 中的静态字段中,具体如下
现在,SkyWalking Agent 启动所需的全部配置都已经填充到 Config 中,后续使用配置信息时直接访问 Config 中的相应静态字段即可。
PluginFinder 对插件进行分类管理
初始化类加载器
在加载插件的第一步就是初始化默认的类加载器。AgentClassLoader.``_initDefaultLoader_``_()_``;
SkyWalking Agent 加载插件时使用到一个自定义的 ClassLoader —— AgentClassLoader,之所以自定义类加载器,目的是不在应用的 Classpath 中引入 SkyWalking 的插件 jar 包,这样就可以让应用无依赖、无感知的插件。
并行加载优化
AgentClassLoader 的静态代码块中会调动 tryRegisterAsParallelCapable() 方法,其中会通过反射方式尝试开启 JDK 的并行加载功能:
要开启并行加载功能,必须要满足以下两个条件:
条件一:被加载的类没有实例被创建
条件二:被加载的类的父类(除了Object),都具有并行加载功能。
一旦一个类加载器开启了并行加载功能,它就不能退回了。代码如下:
/**
* Registers the caller as parallel capable.
* The registration succeeds if and only if all of the following
* conditions are met:
* <ol>
* <li> no instance of the caller has been created</li>
* <li> all of the super classes (except class Object) of the caller are
* registered as parallel capable</li>
* </ol>
* <p>Note that once a class loader is registered as parallel capable, there
* is no way to change it back.</p>
*
* @return true if the caller is successfully registered as
* parallel capable and false if otherwise.
*
* @since 1.7
*/
@CallerSensitive
protected static boolean registerAsParallelCapable() {
Class<? extends ClassLoader> callerClass =
Reflection.getCallerClass().asSubclass(ClassLoader.class);
return ParallelLoaders.register(callerClass);
}
/**
* Registers the given class loader type as parallel capabale.
* Returns {@code true} is successfully registered; {@code false} if
* loader's super class is not registered.
*/
static boolean register(Class<? extends ClassLoader> c) {
synchronized (loaderTypes) {
if (loaderTypes.contains(c.getSuperclass())) {
// register the class loader as parallel capable
// if and only if all of its super classes are.
// Note: given current classloading sequence, if
// the immediate super class is parallel capable,
// all the super classes higher up must be too.
loaderTypes.add(c);
return true;
} else {
return false;
}
}
}
AgentClassLoader 核心实现
在 AgentClassLoader 的构造方法中会初始化其 classpath 字段,该字段指向了 AgentClassLoader 要扫描的目录(skywalking-agent.jar 包同级别的 plugins 目录和 activations 目录),如下所示:
public AgentClassLoader(ClassLoader parent) throws AgentPackageNotFoundException {
super(parent);
File agentDictionary = AgentPackagePath.getPath();
classpath = new LinkedList<>();
// 加载plugins目录
classpath.add(new File(agentDictionary, "plugins"));
// 加载activations目录
classpath.add(new File(agentDictionary, "activations"));
}
AgentClassLoader 作为一个类加载器,主要工作还是从其 Classpath 下加载类(或资源文件),对应的就是其 findClass() 方法和 findResource() 方法。 findClass() 方法的实现如下:
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Jar> allJars = getAllJars();
String path = name.replace('.', '/').concat(".class");
// 扫描所有jar包,查找类文件
for (Jar jar : allJars) {
JarEntry entry = jar.jarFile.getJarEntry(path);
if (entry == null) {
continue;
}
try {
URL classFileUrl = new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + path);
// 读取class文件中的内容
byte[] data;
try (final BufferedInputStream is = new BufferedInputStream(
classFileUrl.openStream()); final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
int ch;
while ((ch = is.read()) != -1) {
baos.write(ch);
}
data = baos.toByteArray();
}
return processLoadedClass(defineClass(name, data, 0, data.length));
} catch (IOException e) {
logger.error(e, "find class fail.");
}
}
throw new ClassNotFoundException("Can't find " + name);
}
findResource的逻辑与findClass类似,代码如下:
@Override
protected URL findResource(String name) {
List<Jar> allJars = getAllJars();
for (Jar jar : allJars) {
JarEntry entry = jar.jarFile.getJarEntry(name);
if (entry != null) {
try {
return new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + name);
} catch (MalformedURLException ignored) {
}
}
}
return null;
}
AgentClassLoader 中有一个 DEFAULT_LOADER 静态字段,记录了 默认的 AgentClassLoader。AgentClassLoader 并不是单例。
解析插件定义
因为有不同的web容器 , 中间件 ,记录的方式也非常的多, 所以SkyWalkingAgent 引入了插件机制,比如 你只想记录 连接数据相关的记录,那么就引入 jdbc 的对应插件即可。
那么 SkyWalkingAgent 是如何加载插件的嗯? 入口就是
org.apache.skywalking.apm.agent.core.plugin.PluginBootstrap#loadPlugins
它会先去 加载 skywalking-plugin.def 文件,然后把skywalking-plugin.def 文件里指定的插件给实例化,然后反回出去
/**
* load all plugins.
*
* @return plugin definition list.
*/
public List<AbstractClassEnhancePluginDefine> loadPlugins() throws AgentPackageNotFoundException {
AgentClassLoader.initDefaultLoader();
//生成一个插件加载器
PluginResourcesResolver resolver = new PluginResourcesResolver();
//去指定的路径下去搜索 文件 skywalking-plugin.def
//指定的路径 默认是 skywalking-agent.jar 同目录的 activations和 plugins 文件夹
List<URL> resources = resolver.getResources();
if (resources == null || resources.size() == 0) {
logger.info("no plugin files (skywalking-plugin.def) found, continue to start application.");
return new ArrayList<AbstractClassEnhancePluginDefine>();
}
//去加载了 skywalking-plugin.def ,并且解析该文件
for (URL pluginUrl : resources) {
try {
PluginCfg.INSTANCE.load(pluginUrl.openStream());
} catch (Throwable t) {
logger.error(t, "plugin file [{}] init failure.", pluginUrl);
}
}
//skywalking-plugin.def 文件里指定的插件的 类给集中保存到容器 pluginClassList里
List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();
List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();
//把上面 pluginClassList 中插件的类 全部给实例化了
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());
}
}
plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));
return plugins;
}
比如: spring-mvc 的插件中 skywalking-plugin.def 的内容如下
apm-springmvc-annotation-5.x-plugin-8.2.0-SNAPSHOT.jar中
spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.ControllerInstrumentation
spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.RestControllerInstrumentation
spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.HandlerMethodInstrumentation
在拿到全部插件的 skywalking-plugin.def 文件之后,PluginCfg 会逐行进行解析,转换成 PluginDefine 对象。PluginDefine 中有两个字段:
/**
* Plugin name.
*/
private String name;
/**
* The class name of plugin defined.
*/
private String defineClass;
PluginCfg 是通过枚举实现的,实现如下 :
public enum PluginCfg {
INSTANCE;
private static final ILog logger = LogManager.getLogger(PluginCfg.class);
private List<PluginDefine> pluginClassList = new ArrayList<PluginDefine>();
void load(InputStream input) throws IOException {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String pluginDefine = null;
while ((pluginDefine = reader.readLine()) != null) {
try {
if (pluginDefine == null || pluginDefine.trim().length() == 0 || pluginDefine.startsWith("#")) {
continue;
}
PluginDefine plugin = PluginDefine.build(pluginDefine);
pluginClassList.add(plugin);
} catch (IllegalPluginDefineException e) {
logger.error(e, "Failed to format plugin({}) define.", pluginDefine);
}
}
} finally {
input.close();
}
}
public List<PluginDefine> getPluginClassList() {
return pluginClassList;
}
}
插件实例化
接下来会遍历全部 PluginDefine 对象,通过反射将其中 defineClass 字段中记录的插件类实例化,核心逻辑如下:
List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();
List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();
//把上面 pluginClassList 中插件的类 全部给实例化了
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());
}
}
AbstractClassEnhancePluginDefine 抽象类是所有 Agent 插件类的顶级父类,其中定义了四个核心方法,决定了一个插件类应该增强哪些目标类、应该如何增强、具体插入哪些逻辑,如下所示:
- enhanceClass() 方法:返回的 ClassMatch,用于匹配当前插件要增强的目标类。
- define() 方法:插件类增强逻辑的入口,底层会调用下面的 enhance() 方法和 witnessClass() 方法。
- enhance() 方法:真正执行增强逻辑的地方。
- witnessClass() 方法:一个开源组件可能有多个版本,插件会通过该方法识别组件的不同版本,防止对不兼容的版本进行增强。
ClassMatch
enhanceClass() 方法决定了一个插件类要增强的目标类,返回值为 ClassMatch 类型对象。ClassMatch 类似于一个过滤器,可以通过多种方式匹配到目标类,ClassMatch 接口的实现如下:
NameMatch: 根据类名进行匹配
IndirectMatch:间接匹配,由子类进行实现,接口中定义了两个方法
/**
* All implementations can't direct match the class like {@link NameMatch} did.
*/
public interface IndirectMatch extends ClassMatch {
// Junction是Byte Buddy中的类,可以通过and、or等操作串联多个ElementMatcher
// 进行匹配
ElementMatcher.Junction buildJunction();
// 用于检测传入的类型是否匹配该Match
boolean isMatch(TypeDescription typeDescription);
MultiClassNameMatch:其中会指定一个 matchClassNames 集合,该集合内的类即为目标类。
ClassAnnotationMatch:根据标注在类上的注解匹配目标类。
MethodAnnotationMatch:根据标注在方法上的注解匹配目标类。
HierarchyMatch:根据父类或是接口匹配目标类。
LogicalOrMatch: 根据多个逻辑或进行匹配。
LogicalAndMatch:根据多个逻辑与进行匹配。
PrefixMatch:根据指定前缀进行匹配。
下面以ClassAnnotationMatch为例子。该类的功能是根据指定类上的注解来进行匹配。
其中的 annotations 字段指定了该 ClassAnnotationMatch 对象需要检查的注解。在 buildJunction() 方法中将为每一个注解创建相应的 Junction 并将它们以 and 形式连接起来并返回,如下所示:
@Override
public ElementMatcher.Junction buildJunction() {
ElementMatcher.Junction junction = null;
for (String annotation : annotations) {
if (junction == null) {
junction = buildEachAnnotation(annotation);
} else {
junction = junction.and(buildEachAnnotation(annotation));
}
}
junction = junction.and(not(isInterface()));
return junction;
}
isMatch() 方法只有包含所有指定注解的类,才能匹配成功,如下所示:
@Override
public boolean isMatch(TypeDescription typeDescription) {
List<String> annotationList = new ArrayList<String>(Arrays.asList(annotations));
AnnotationList declaredAnnotations = typeDescription.getDeclaredAnnotations();
for (AnnotationDescription annotation : declaredAnnotations) {
annotationList.remove(annotation.getAnnotationType().getActualName());
}
return annotationList.isEmpty();
}
查找插件PluginFinder
PluginFinder 是 AbstractClassEnhancePluginDefine 查找器,可以根据给定的类查找用于增强的 AbstractClassEnhancePluginDefine 集合。
在 PluginFinder 的构造函数中会遍历前面课程已经实例化的 AbstractClassEnhancePluginDefine ,并根据 enhanceClass() 方法返回的 ClassMatcher 类型进行分类,得到如下三个集合:
/**
* 如果返回值为NameMatch类型,则相应 AbstractClassEnhancePluginDefine
* 对象会记录到该集合
*/
private final Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<AbstractClassEnhancePluginDefine>>();
/**
* 如果是其他类型返回值,则相应 AbstractClassEnhancePluginDefine
* 对象会记录到该集合
*/
private final List<AbstractClassEnhancePluginDefine> signatureMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
/**
* 是否为启动的仪表插件
*/
private final List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
构造函数如下:
public PluginFinder(List<AbstractClassEnhancePluginDefine> plugins) {
for (AbstractClassEnhancePluginDefine plugin : plugins) {
ClassMatch match = plugin.enhanceClass();
if (match == null) {
continue;
}
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);
}
if (plugin.isBootstrapInstrumentation()) {
bootstrapClassMatchDefine.add(plugin);
}
}
}
find() 方法是 PluginFinder 对外暴露的查询方法,其中会先后遍历 nameMatchDefine 集合和 signatureMatchDefine 集合,通过 ClassMatch.isMatch() 方法确定所有的匹配插件。
public List<AbstractClassEnhancePluginDefine> find(TypeDescription typeDescription) {
List<AbstractClassEnhancePluginDefine> matchedPlugins = new LinkedList<AbstractClassEnhancePluginDefine>();
String typeName = typeDescription.getTypeName();
if (nameMatchDefine.containsKey(typeName)) {
matchedPlugins.addAll(nameMatchDefine.get(typeName));
}
for (AbstractClassEnhancePluginDefine pluginDefine : signatureMatchDefine) {
IndirectMatch match = (IndirectMatch) pluginDefine.enhanceClass();
if (match.isMatch(typeDescription)) {
matchedPlugins.add(pluginDefine);
}
}
return matchedPlugins;
}
使用 Byte Buddy 库创建 AgentBuilder
创建ByteBuddy的代码如下。这里使用了IS_OPEN_DEBUGGING_CLASS变量,如果配置为 true,则会将动态生成的类输出到 debugging 目录中。
final ByteBuddy byteBuddy = new ByteBuddy()
.with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
/**
* If true, SkyWalking agent will save all instrumented classes files in `/debugging` folder. SkyWalking team
* may ask for these files in order to resolve compatible problem.
*/
public static boolean IS_OPEN_DEBUGGING_CLASS = false;
接下来创建 AgentBuilder 对象,AgentBuilder 是 Byte Buddy 库专门用来支持 Java Agent 的一个 API。代码里用到的方法定义如下 :
- ignore() 方法:忽略指定包中的类,对这些类不会进行拦截增强。
- type() 方法:在类加载时根据传入的 ElementMatcher 进行拦截,拦截到的目标类将会被 transform() 方法中指定的 Transformer 进行增强。
- transform() 方法:这里指定的 Transformer 会对前面拦截到的类进行增强。
- with() 方法:添加一个 Listener 用来监听 AgentBuilder 触发的事件。
// 使用 Byte Buddy 库创建 AgentBuilder
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"))
// 处理 Skywalking 的类
.or(allSkyWalkingAgentExcludeToolkit())
// synthetic类和方法是由编译器生成的,这种类也需要忽略
.or(ElementMatchers.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;
}
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.");
}
}
agentBuilder.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new Listener())
.installOn(instrumentation);
由于在JDK9中开始引入了模块的概念,为了对这个进行支持,也进行了专门的处理。
PluginFInder.buildMatch() 方法返回的 ElementMatcher 对象会将全部插件的匹配规则(即插件的 enhanceClass() 方法返回的 ClassMatch)用 OR 的方式连接起来,这样,所有插件能匹配到的所有类都会交给 Transformer 处理,代码如下:
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());
}
}
return new ProtectiveShieldMatcher(judge);
}
with() 方法中添加的监听器 —— SkywalkingAgent.Listener,它继承了 AgentBuilder.Listener 接口,当监听到 Transformation 事件时,会根据 IS_OPEN_DEBUGGING_CLASS 配置决定是否将增强之后的类持久化成 class 文件保存到指定的 log 目录中。注意,该操作是需要加锁的,会影响系统的性能,一般只在测试环境中开启,在生产环境中不会开启。
Skywalking.Transformer,它实现了 AgentBuilder.Transformer 接口,其 transform() 方法是插件增强目标类的入口。Skywalking.Transformer 会通过 PluginFinder 查找目标类匹配的插件(即 AbstractClassEnhancePluginDefine 对象),然后交由 AbstractClassEnhancePluginDefine 完成增强。
@Override
public DynamicType.Builder<?> transform(final DynamicType.Builder<?> builder,
final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule module) {
List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
if (pluginDefines.size() > 0) {
DynamicType.Builder<?> newBuilder = builder;
EnhanceContext context = new EnhanceContext();
for (AbstractClassEnhancePluginDefine define : pluginDefines) {
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;
}
加载 BootService
SkyWalking Agent 启动的最后一步是使用前面介绍的 JDK SPI 技术加载 BootService 接口的所有实现类,BootService 接口中定义了 SkyWalking Agent 核心服务的行为。
/**
* 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;
}
ServiceManager 是 BootService 实例的管理器,主要负责管理 BootService 实例的生命周期。
ServiceManager 是个单例,底层维护了一个 bootedServices 集合(private Map<Class, BootService> bootedServices = Collections.emptyMap();),记录了每个 BootService 实现对应的实例。boot() 方法是 ServiceManager 的核心方法,它首先通过 load() 方法实例化全部 BootService 接口实现。它通过 JDK SPI 技术加载并实例化 META-INF/service下的BootService接口实现。
void load(List<BootService> allServices) {
for (final BootService bootService : ServiceLoader.load(BootService.class, AgentClassLoader.getDefault())) {
allServices.add(bootService);
}
}
org.apache.skywalking.apm.agent.core.remote.TraceSegmentServiceClient
org.apache.skywalking.apm.agent.core.context.ContextManager
org.apache.skywalking.apm.agent.core.sampling.SamplingService
org.apache.skywalking.apm.agent.core.remote.GRPCChannelManager
org.apache.skywalking.apm.agent.core.jvm.JVMMetricsSender
org.apache.skywalking.apm.agent.core.jvm.JVMService
org.apache.skywalking.apm.agent.core.remote.ServiceManagementClient
org.apache.skywalking.apm.agent.core.context.ContextManagerExtendService
org.apache.skywalking.apm.agent.core.commands.CommandService
org.apache.skywalking.apm.agent.core.commands.CommandExecutorService
org.apache.skywalking.apm.agent.core.profile.ProfileTaskChannelService
org.apache.skywalking.apm.agent.core.profile.ProfileSnapshotSender
org.apache.skywalking.apm.agent.core.profile.ProfileTaskExecutionService
org.apache.skywalking.apm.agent.core.meter.MeterService
org.apache.skywalking.apm.agent.core.meter.MeterSender
加载完上述 BootService 实现类型之后,ServiceManager 会针对 BootService 上的 @DefaultImplementor 和 @OverrideImplementor 注解进行处理:
- @DefaultImplementor 标识 BootService 接口的默认实现。
- @OverrideImplementor 覆盖默认 BootService 实现,通过其 value 字段指定要覆盖的默认实现。
确定完要使用的 BootService 实现之后,ServiceManager 将统一初始化 bootServices 集合中的 BootService 实现,同样是在 ServiceManager.boot() 方法中,会逐个调用 BootService 实现的 prepare()、startup()、onComplete() 方法,
public void boot() {
bootedServices = loadAllServices();
prepare();
startup();
onComplete();
}
JVM 退出钩子
// 添加一个JVM钩子
Runtime.getRuntime()
.addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread"));
在 Skywalking Agent 启动流程的最后,会添加一个 JVM 退出钩子,并通过 ServiceManager.shutdown() 方法,关闭前文启动的全部 BootService 服务。