SkyWalking Agent realization principle

Microkernel architecture


SkyWalking Agent uses Microkernel Architecture. What is Microkernel Architecture? The microkernel architecture is also known as Plug-in Architecture, which is a scalable architecture that is functionally split. Microkernel architecture is usually used in product-based applications. For example, IDE development tools such as IDEA and Eclipse, the kernel is very streamlined, and support for new functions such as Maven and Gradle are added in the form of plug-ins.

As shown in the figure below, the microkernel architecture is divided into two parts: the core system and the plug-in module.

In the micro-kernel architecture shown in the figure above, the kernel function is relatively stable, it is only responsible for managing the life cycle of the plug-in, and will not be continuously modified due to the expansion of system functions. Functional extensions are all encapsulated in plug-ins. Plug-in modules are independent modules that contain specific functions and can expand the functions of the core system. Generally, different plug-in modules are independent of each other. Of course, you can design a plug-in to depend on another plug-in, but you should try to minimize the interdependence between plug-ins to avoid complicated dependencies that cause scalability problems.

Finally, all plug-ins will be unified access and management by the kernel system:

1. The kernel system must know which plug-ins to load. Generally, the plug-ins to be loaded are determined through configuration files or scanning ClassPath (such as the SPI technology introduced above);
2. The kernel system also needs to know how to use these plug-ins, microkernel The architecture needs to define a set of plug-in specifications, and the kernel system will initialize and start these plug-ins in a unified way;
3. Although the plug-ins are completely decoupled, there are always some unexpected requirements in the actual development that will cause the plug-ins Dependence is generated or some underlying plug-ins are reused. At this time, the kernel needs to provide a set of rules to identify plug-in messages and correctly forward messages between plug-ins, and become a transfer station for plug-in messages.

This shows the benefits of microkernel architecture:

Test costs are reduced. From the perspective of software engineering, the microkernel architecture splits the changed part from the unchanging part, which reduces the cost of testing and conforms to the open and closed principle in the design mode.
stability. Since each plug-in module is relatively independent, even if one of the plug-ins has a problem, the stability of the kernel system and other plug-ins can be guaranteed.
Scalability. When adding new functions or accessing new services, you only need to add the corresponding plug-in module; when the historical function is offline, you only need to delete the corresponding plug-in module.
SkyWalking Agent is a landing method of microkernel architecture. In the previous class, I have introduced the functions of each module in SkyWalking. The apm-agent-core module corresponds to the kernel system in the micro-kernel architecture, and the sub-modules in the apm-sdk-plugin module are all in the micro-kernel architecture. Plug-in module.

Overview of the SkyWalking Agent startup process.
Previously, at the end of building the SkyWalking source code environment, we tried to debug the SkyWalking Agent source code. The entrance was the premain() method of the SkyWalkingAgent class in the apm-agent module, which completed the agent startup process:

  1. Initialize configuration information. In this step, the agent.config configuration file will be loaded, which will detect whether the Java Agent parameters and environment variables cover the corresponding configuration items.
  2. Find and parse the skywalking-plugin.def plugin file.
  3. AgentClassLoader loads the plug-in.
  4. PluginFinder classifies and manages plug-ins.
  5. Use Byte Buddy library to create AgentBuilder. Here, the target class will be dynamically enhanced according to the loaded plug-in, and the embedded point logic will be inserted.
  6. Use JDK SPI to load and start the BootService service. The implementation of the BootService interface will be introduced in detail in a later lesson.
  7. Add a JVM hook to close all BootService services when the JVM exits.

The specific implementation of the SkywalkingAgent.premain() method is as follows, with the try/catch code block and exception handling logic omitted:

public static void premain(String agentArgs, 

       Instrumentation instrumentation) throws PluginException {

    // 步骤1、初始化配置信息

    SnifferConfigInitializer.initialize(agentArgs); 

    // 步骤2~4、查找并解析skywalking-plugin.def插件文件;

    // AgentClassLoader加载插件类并进行实例化;PluginFinder提供插件匹配的功能

    final PluginFinder pluginFinder = new PluginFinder(

       new PluginBootstrap().loadPlugins());

    // 步骤5、使用 Byte Buddy 库创建 AgentBuilder

    final ByteBuddy byteBuddy = new ByteBuddy()

       .with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));

    new AgentBuilder.Default(byteBuddy)...installOn(instrumentation);

    // 这里省略创建 AgentBuilder的具体代码,后面展开详细说

    // 步骤6、使用 JDK SPI加载的方式并启动 BootService 服务。

    ServiceManager.INSTANCE.boot();

    // 步骤7、添加一个JVM钩子

    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

      public void run() { ServiceManager.INSTANCE.shutdown(); }

    }, "skywalking service shutdown thread"));

}

After understanding the core steps of SkyWalking Agent startup, the rest of this lesson will conduct an in-depth analysis of each step.

Initial configuration


When launching two demo applications, demo-webapp and demo-provider, you need to specify the agent.confg configuration file (skywalking_config parameter) in VM options. The configuration items in the agent.config configuration file are as follows:

# 当前应用的服务名称,通过Skywalking Agent上报的Metrics、Trace数据都会

# 携带该信息进行标识

agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}

In the SnifferConfigInitializer.initialize() method, the final configuration information will be filled into the static fields of Config. The filling process is as follows:

Fill all the configuration information in the agent.config file into the corresponding static fields in Config.
Analyze the value of system environment variables and overwrite the corresponding static fields in Config.
Parse the parameters of Java Agent and overwrite the corresponding static fields in Config.
The specific implementation of the SnifferConfigInitializer.initialize() method is as follows:

public static void initialize(String agentOptions) {

    // 步骤1、加载 agent.config配置文件

    InputStreamReader configFileStream = loadConfig();

    Properties properties = new Properties();

    properties.load(configFileStream);

    for (String key : properties.stringPropertyNames()) {

        String value = (String)properties.get(key);

        // 按照${配置项名称:默认值}的格式解析各个配置项

        properties.put(key, PropertyPlaceholderHelper.INSTANCE

            .replacePlaceholders(value, properties));

    }

    // 填充 Config中的静态字段

    ConfigInitializer.initialize(properties, Config.class);

    // 步骤2、解析环境变量,并覆盖 Config中相应的静态字段

    overrideConfigBySystemProp();

    // 步骤3、解析 Java Agent参数,并覆盖 Config中相应的静态字段

    overrideConfigByAgentOptions(agentOptions);

    // 检测SERVICE_NAME和BACKEND_SERVICE两个配置项,若为空则抛异常(略)

    IS_INIT_COMPLETED = true; // 更新初始化标记

}

The loadConfig() method in step 1 will be loaded first according to the agent.config file path specified by the environment variable (skywalking_config). If the environment variable does not specify the skywalking_ config configuration, search the agent.confg configuration file in the config directory at the same level as skywalking-agent.jar.

After loading the configuration information in the agent.config file to the Properties object, PropertyPlaceholderHelper will be used to parse the configuration information, and the current configuration value in the format of "${configuration item name: default value}" will be replaced with the default value. The demo-provider analysis result is shown in the following figure:

After the analysis is completed, the configuration information will be filled into the static fields in Config through the ConfigInitializer tool class. The specific filling rules are as follows:

In the following overrideConfigBySystemProp() method, the environment variables (ie the System.getProperties() collection) will be traversed. If the environment change starts with "skywalking.", it is considered to be the configuration of SkyWalking, which will also be filled in the Config class. To override the default value in agent.config.

The last overrideConfigByAgentOptions() method parses the parameters of the Java Agent. The rules for filling the Config class are the same as the previous two steps and will not be repeated.

Up to this point, all the configurations required for SkyWalking Agent startup have been filled in Config, and you can directly access the corresponding static fields in Config when using configuration information later.

Plug-in loading principle


After completing the initialization of the Config class, SkyWalking Agent starts scanning and loading the SkyWalking Agent plug-in jar package in the specified directory.

AgentClassLoader


SkyWalking Agent uses a custom ClassLoader —— AgentClassLoader when loading plug-ins. The reason for customizing the class loader is to not introduce SkyWalking plug-in jar packages in the application’s Classpath, so that the application can be dependent and unaware of plug-ins. .

Parallel load optimization


The static code block of AgentClassLoader will call the tryRegisterAsParallelCapable() method, which will try to enable the parallel loading function of the JDK through reflection:

private static void tryRegisterAsParallelCapable() {

    Method[] methods = ClassLoader.class.getDeclaredMethods();

    for (int i = 0; i < methods.length; i++) {

        Method method = methods[i];

        String methodName = method.getName();

        // 查找 ClassLoader中的registerAsParallelCapable()静态方法

        if ("registerAsParallelCapable".equalsIgnoreCase(methodName)) 

        {

            method.setAccessible(true);

            method.invoke(null); // 调用registerAsParallelCapable()方法

            return;

        }

    }

}


When using ClassLoader to load a class, the JVM will perform lock synchronization, which is why we can use the class loading mechanism to implement singletons. In Java 6, the ClassLoader.loadClass() method is synchronized with synchronized locking, which requires global competition for a lock, which is slightly inefficient.

Two locking modes are provided after Java 7:

In serial mode, the locked object is the ClassLoader itself, which has the same behavior as in Java 6. The
other is the parallel loading mode that is turned on after calling the registerAsParallelCapable() method. When loading a class in parallel mode, the lock will be acquired according to the classname. The corresponding implementation fragment in the ClassLoader.loadClass() method is as follows:

protected Class<?> loadClass(String name, boolean resolve)

    throws ClassNotFoundException{

    // getClassLoadingLock() 方法会返回加锁的对象

    synchronized (getClassLoadingLock(name)) { 

       ... ... // 加载指定类,具体加载细节不展开介绍

    }

}

protected Object getClassLoadingLock(String className) {

    Object lock = this;

    if (parallelLockMap != null) { // 检测是否开启了并行加载功能

        Object newLock = new Object();

        // 若开启了并行加载,则一个className对应一把锁;否则还是只

        // 对当前ClassLoader进行加锁

        lock = parallelLockMap.putIfAbsent(className, newLock);

        if (lock == null) {

            lock = newLock;

        }

    }

    return lock;

}


AgentClassLoader core implementation
The classpath field is initialized in the construction method of AgentClassLoader, which points to the directory that AgentClassLoader will scan (the plugins directory and activations directory at the same level as the skywalking-agent.jar package), as shown below:

private List<File> classpath; 

public AgentClassLoader(ClassLoader parent) {

    super(parent); // 双亲委派机制

    // 获取 skywalking-agent.jar所在的目录

    File agentDictionary = AgentPackagePath.getPath();

    classpath = new LinkedList<File>();

    // 初始化 classpath集合,指向了skywalking-agent.jar包同目录的两个目录

    classpath.add(new File(agentDictionary, "plugins"));

    classpath.add(new File(agentDictionary, "activations"));

}


As a class loader, AgentClassLoader's main job is to load classes (or resource files) from its Classpath, corresponding to its findClass() method and findResource() method. Here is a brief look at the implementation of the findClass() method:

// 在下面的getAllJars()方法中会扫描全部jar文件,并缓存到

// allJars字段(List<Jar>类型)中,后续再次扫描时会重用该缓

private List<Jar> allJars;

protected Class<?> findClass(String name) {

    List<Jar> allJars = getAllJars();  // 扫描过程比较简单,不再展开介绍

    String path = name.replace('.', '/').concat(".class");

    for (Jar jar : allJars) { // 扫描所有jar包,查找类文件

        JarEntry entry = jar.jarFile.getJarEntry(path);

        if (entry != null) {

            URL classFileUrl = new URL("jar:file:" + 

                jar.sourceFile.getAbsolutePath() + "!/" + path);

            byte[] data = ...;// 省略读取".class"文件的逻辑                          // 加载类文件内容,创建相应的Class对象

            return defineClass(name, data, 0, data.length);

        }

    } // 类查找失败,直接抛出异常

    throw new ClassNotFoundException("Can't find " + name);

}


The findResource() method will traverse all jar packages cached in the allJars collection, find the specified resource file and return it. The traversal logic is similar to that of the findClass() method, and no further analysis will be performed.

Finally, there is a DEFAULT_LOADER static field in AgentClassLoader, which records the default AgentClassLoader, as shown below, but note that AgentClassLoader is not a singleton. You will see other places where AgentClassLoader is created later.

private static AgentClassLoader DEFAULT_LOADER;


Parse the plugin definition


Each Agent plug-in will define a skywalking-plugin.def file, as shown in the following figure tomcat-7.x-8.x-plugin:

The content of the skywalking-plugin.def file in the tomcat-7.x-8.x-plugin plug-in is as follows, each line of which is the definition of a plug-in class:

tomcat-7.x/8.x=org.apache.skywalking.apm.plugin.tomcat78x.define 

.TomcatInstrumentation

tomcat-7.x/8.x=org.apache.skywalking.apm.plugin.tomcat78x.define

.ApplicationDispatcherInstrumentation


PluginResourcesResolver is the resource resolver of the Agent plug-in. It will read the skywalking-plugin.def file in all Agent plug-ins through the findResource() method in AgentClassLoader.

After AbstractClassEnhancePluginDefine
gets the skywalking-plugin.def file of all plug-ins, PluginCfg will parse it line by line and convert it into a PluginDefine object. There are two fields in PluginDefine:

// 插件名称,以 tomcat-7.x-8.x-plugin 插件第一行为例,就是tomcat-7.x/8.x

private String name; 

// 插件类,对应上例中的 org.apache.skywalking.apm.plugin.tomcat78x.define

// .TomcatInstrumentation

private String defineClass;

PluginCfg is a singleton tool class implemented through enumeration. The logic is very simple and will not be introduced.

Next, it will traverse all PluginDefine objects and instantiate the plug-in class recorded in the defineClass field through reflection. The core logic is as follows:

for (PluginDefine pluginDefine : pluginClassList) {

    // 注意,这里使用类加载器是默认的AgentClassLoader实例

    AbstractClassEnhancePluginDefine plugin =

        (AbstractClassEnhancePluginDefine)

            Class.forName(pluginDefine.getDefineClass(), true,

            AgentClassLoader.getDefault()).newInstance();

    plugins.add(plugin); // 记录AbstractClassEnhancePluginDefine 对象

}


The AbstractClassEnhancePluginDefine abstract class is the top-level parent class of all Agent plug-in classes. It defines four core methods, which determine which target classes a plug-in class should enhance, how to enhance, and which logic to insert, as shown below:

  • enhanceClass() method: The returned ClassMatch is used to match the target class to be enhanced by the current plug-in.
  • define() method: the entrance of the plug-in class enhancement logic, the bottom layer will call the following enhancement() method and witnessClass() method.
  • The enhance() method: where the enhancement logic is actually executed.
  • WitnessClass() method: An open source component may have multiple versions, and the plug-in will use this method to identify different versions of the component to prevent incompatible versions from being enhanced.

The specific function and implementation of each method will be introduced in detail in the subsequent class. You should know that there are roughly four methods in AbstractClassEnhancePluginDefine.

The ClassMatch
enhanceClass() method determines the target class to be enhanced by a plug-in class, and the return value is a ClassMatch type object. ClassMatch is similar to a filter, which can be matched to the target class in many ways. The implementation of the ClassMatch interface is as follows:

**NameMatch:** Match the name of the target class according to its className field (String type).
**IndirectMatch:** Two methods are defined in the subinterface.

// Junction是Byte Buddy中的类,可以通过and、or等操作串联多个ElementMatcher

// 进行匹配

ElementMatcher.Junction buildJunction(); 

// 用于检测传入的类型是否匹配该Match

boolean isMatch(TypeDescription typeDescription);


MultiClassNameMatch: It will specify a matchClassNames collection, and the classes in this collection are the target classes.
ClassAnnotationMatch: Match the target class according to the annotations marked on the class.
MethodAnnotationMatch: Match the target class according to the annotations marked on the method.
HierarchyMatch: Match the target class according to the parent class or interface.
Here we take ClassAnnotationMatch as an example to analyze. The annotations field (String[] type) specifies the annotations to be checked for this ClassAnnotationMatch object. In the buildJunction() method, a corresponding Junction will be created for each annotation and connected in and form and returned, as shown below:

public ElementMatcher.Junction buildJunction() {

    ElementMatcher.Junction junction = null;

    for (String annotation : annotations) { // 遍历全部注解

        if (junction == null) { 

            // 该Junction用于检测类是否标注了指定注解

            junction = buildEachAnnotation(annotation);

        } else {// 使用 and 方式将所有Junction对象连接起来

            junction = junction.and(buildEachAnnotation(annotation));

        }

    }

    junction = junction.and(not(isInterface())); // 排除接口

    return junction;

}


The implementation of the isMatch() method is similar. Only the class that contains all specified annotations can be matched successfully, as shown below:

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());

    }

    if (annotationList.isEmpty()) { // 删空了,就匹配成功了

        return true;

    }

    return false;

}


The implementation principles of other ClassMatch interfaces are similar, so we will not analyze it anymore. If you are interested, you can take a look at the code.

PluginFinder
PluginFinder is an AbstractClassEnhancePluginDefine finder, which can find the AbstractClassEnhancePluginDefine collection for enhancement according to a given class.

In the constructor of PluginFinder, the AbstractClassEnhancePluginDefine that has been instantiated in the previous course will be traversed, and classified according to the ClassMatcher type returned by the enhanceClass() method, and the following two sets will be obtained:

// 如果返回值为NameMatch类型,则相应 AbstractClassEnhancePluginDefine 

// 对象会记录到该集合

private Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine;

// 如果是其他类型返回值,则相应 AbstractClassEnhancePluginDefine 

// 对象会记录到该集合

private List<AbstractClassEnhancePluginDefine> signatureMatchDefine;


The find() method is a query method exposed by PluginFinder, which traverses the nameMatchDefine collection and the signatureMatchDefine collection successively, and determines all matching plugins through the ClassMatch.isMatch() method. The implementation of find() method is not complicated, so I won't introduce it again.

AgentBuilder
has analyzed the steps of loading configuration information, initializing the Config class, searching the skywalking-pluing.def file, and initializing the AbstractClassEnhancePluginDefine object during the startup of the Skywalking Agent. Now let’s introduce how Byte Buddy uses the loaded plug-in class to enhance the target method.

In step 5 of the SkywalkingAgent.premain() method, the ByteBuddy object will be created first. As mentioned in the Byte Buddy basic lesson, it is one of the basic objects of Byte Buddy:

// 步骤5、通过Byte Buddy API创建Agent

final ByteBuddy byteBuddy = new ByteBuddy()

   .with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));


The corresponding configuration items of Config.Agent.IS_OPEN_DEBUGGING_CLASS in the agent.config file are:

agent.is_open_debugging_class


If it is configured as true, the dynamically generated classes will be output to the debugging directory.

Next, create an AgentBuilder object, which is an API of the Byte Buddy library specifically to support Java Agent, as shown below:

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())// 拦截

.transform(new Transformer(pluginFinder)) // 设置Transform

.with(new Listener()) // 设置Listener

.installOn(instrumentation)

Briefly explain the method of AgentBuilder used here:

  • Ignore() method: Ignore the classes in the specified package, and no interception enhancement will be performed on these classes.
  • type() method: interception according to the incoming ElementMatcher when the class is loaded, the intercepted target class will be enhanced by the Transformer specified in the transform() method.
  • transform() method: The Transformer specified here will enhance the previously intercepted class.
  • with() method: Add a Listener to listen to events triggered by AgentBuilder.

First of all, the ElementMatcher object returned by the PluginFInder.buildMatch() method will connect the matching rules of all plug-ins (that is, the ClassMatch returned by the enhanceClass() method of the plug-in) by OR. In this way, all classes that can be matched by all plug-ins will be crossed. Handled to Transformer.

Let’s look at the listener added in the with() method—SkywalkingAgent.Listener, which inherits the AgentBuilder.Listener interface. When a Transformation event is monitored, it will decide whether to persist the enhanced class into a class file and save it to the specified according to the IS_OPEN_DEBUGGING_CLASS configuration. In the log directory. Note that this operation needs to be locked, which will affect the performance of the system. Generally, it is only turned on in a test environment, but not in a production environment.

Finally, look at Skywalking.Transformer, which implements the AgentBuilder.Transformer interface, and its transform() method is the entrance to the plug-in enhancement target class. Skywalking.Transformer will use PluginFinder to find the plug-in that matches the target class (ie AbstractClassEnhancePluginDefine object), and then hand it over to AbstractClassEnhancePluginDefine to complete the enhancement. The core implementation is as follows:

public DynamicType.Builder<?> transform(DynamicType.Builder<?>builder,

    TypeDescription typeDescription, // 被拦截的目标类

    ClassLoader classLoader,  // 加载目标类的ClassLoader

    JavaModule module) {

    // 从PluginFinder中查找匹配该目标类的插件,PluginFinder的查找逻辑不再重复

    List<AbstractClassEnhancePluginDefine> pluginDefines =

           pluginFinder.find(typeDescription);

    if (pluginDefines.size() >0){ 

        DynamicType.Builder<?>newBuilder = builder;

        EnhanceContext context = new EnhanceContext();

        for (AbstractClassEnhancePluginDefinedefine : pluginDefines) {

            // AbstractClassEnhancePluginDefine.define()方法是插件入口,

            // 在其中完成了对目标类的增强

            DynamicType.Builder<?>possibleNewBuilder = 

                 define.define(typeDescription, 

                      newBuilder, classLoader,context);

            if (possibleNewBuilder != null) {

                // 注意这里,如果匹配了多个插件,会被增强多次

                newBuilder = possibleNewBuilder;

            }

        }

        return newBuilder;

    }

    return builder;

}


Note here: If a class is matched by multiple plug-ins, it will be enhanced multiple times. When you open the IS_OPEN_DEBUGGING_CLASS configuration item, you will see multiple corresponding class files.

Load BootService
The last step of SkyWalking Agent startup is to use the JDK SPI technology introduced earlier to load all implementation classes of the BootService interface. The BootService interface defines the behavior of the SkyWalking Agent core service, which is defined as follows:

public interface BootService {

    void prepare() throws Throwable;

    void boot() throws Throwable;

    void onComplete() throws Throwable;

    void shutdown() throws Throwable;

}


ServiceManager is the manager of BootService instance, mainly responsible for managing the life cycle of BootService instance.

ServiceManager is a singleton. The bottom layer maintains a bootedServices collection (Map<Class, BootService> type), and records the corresponding instance of each BootService implementation. The boot() method is the core method of ServiceManager. It first instantiates all BootService interface implementations through the load() method, as shown below:

void load(List<BootService> allServices) { 

    // 很明显使用了 JDK SPI 技术加载并实例化 META-INF/services下的全部 

    // BootService接口实现

    Iterator<BootService> iterator = ServiceLoader.load(

        BootService.class,AgentClassLoader.getDefault()).iterator();

    while (iterator.hasNext()) {

        // 记录到方法参数传入的 allServices集合中

        allServices.add(iterator.next()); 

    }

}


In the resource/META-INF.services/org.apache.skywalking.apm.agent.core.boot.BootService file of the apm-agent-core module, the BootService interface implementation classes to be loaded by the ServiceManager are recorded, as shown below. The class will introduce its specific functions in detail in the following class:

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.JVMService

org.apache.skywalking.apm.agent.core.remote.ServiceAndEndpointRegisterClient

org.apache.skywalking.apm.agent.core.context.ContextManagerExtendService


After loading the above BootService implementation types, ServiceManager will process the @DefaultImplementor and @OverrideImplementor annotations on BootService:

  • The @DefaultImplementor annotation is used to identify the default implementation of the BootService interface.
  • The @OverrideImplementor annotation is used to override the default BootService implementation. The value field specifies the default implementation to be overridden.

The overlay logic of BootService is shown in the figure below:

After determining the BootService implementation to be used, the ServiceManager will uniformly initialize the BootService implementation in the bootServices collection. Also in the ServiceManager.boot() method, the prepare(), startup(), and onComplete() methods implemented by the BootService will be called one by one. The specific implementation is as follows:

public void boot() {

    bootedServices = loadAllServices(); 

    prepare(); // 调用全部BootService对象的prepare()方法

    startup(); // 调用全部BootService对象的boot()方法

    onComplete(); // 调用全部BootService对象的onComplete()方法

}


At the end of the Skywalking Agent startup process, a JVM exit hook will be added, and all BootService services started in the previous section will be shut down through the ServiceManager.shutdown() method.

The relevant code snippets in the SkywalkingAgent.premain() method are as follows:

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

    @Override public void run() {

        ServiceManager.INSTANCE.shutdown(); 

    }

}, "skywalking service shutdown thread"));

 

Guess you like

Origin blog.csdn.net/lewee0215/article/details/109636349