SkyWalking源码-- Agent 加载插件

366 阅读3分钟

本文基于 SkyWalking-Java-agent 8.15.0 版本

// 2.加载插件  
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());

PluginBootstrap 类里面只有一个方法,用于加载所有的插件

image.png

插件加载核心方法:loadPlugins()

/**  
* load all plugins.  
*  
* @return plugin definition list.  
*/  
public List<AbstractClassEnhancePluginDefine> loadPlugins() throws AgentPackageNotFoundException {  
    // 初始化自定义的类加载器  
    AgentClassLoader.initDefaultLoader();  
  
    PluginResourcesResolver resolver = new PluginResourcesResolver();  
    // 1)拿到所有skywalking-plugin.def的资源  
    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>();  
    }  
    // 2)遍历  
    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();  

    List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();  
    for (PluginDefine pluginDefine : pluginClassList) {  
        try {  
            LOGGER.debug("loading plugin class {}.", pluginDefine.getDefineClass());  
            // 3)通过AgentClassLoader 实例化插件定义类实例  
            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());  
        }  
    }  
    // 4)加载基于xml定义的插件  
    plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));  

    return plugins;  
  
}

插件加载的体系结构包含以下重要的知识点:

image.png

并行类加载器

image.png

// 初始化自定义的类加载器  
AgentClassLoader.initDefaultLoader();

public class AgentClassLoader extends ClassLoader

AgentClassLoader 继承自 JDK 的 java.lang.ClassLoader,是 SkyWalking 自定义的类加载器,负责查找插件和拦截器。在该类的 static代码块里使用 registerAsParallelCapable 方法,将类加载器注册为具备并行能力。
类加载器注册为具备并行能力,需要同时满足两个条件,注册才能成功:

  1. 调用该方法的类加载器实例还没有创建
  2. 调用该方法的类加载器所有父类(Object类除外)都注册为具备并行能力

注意:一旦类加载器被注册为并行能力,就不能被改变回串行能力

  1. 默认情况下,类加载器以自身为锁,串行加载类
  2. 先将类注册为并行能力,可以实现类的并行加载。并行加载的实现方式是在 parallelLockMap 里面存放加载类相关的锁信息,类加载过程的锁粒度细化到类级别
// 获取类加载时的锁
protected Object getClassLoadingLock(String className) {  
    Object lock = this;  
    // 并行锁
    // 并行锁不存在时,默认使用类加载器所谓锁
    if (parallelLockMap != null) {  
        // 并行锁存在时,以加载类区分不同的锁
        Object newLock = new Object();  
        lock = parallelLockMap.putIfAbsent(className, newLock);  
        if (lock == null) {  
            lock = newLock;  
        }  
    }  
    return lock;  
}
/**  
* The default class loader for the agent.  
*/  
private static AgentClassLoader DEFAULT_LOADER;  
  
private List<File> classpath;  
private List<Jar> allJars;  
private ReentrantLock jarScanLock = new ReentrantLock();  
  
public static AgentClassLoader getDefault() {  
    return DEFAULT_LOADER;  
}  
  
/**  
* Init the default class loader. 
* 初始化默认类加载器
*  
* @throws AgentPackageNotFoundException if agent package is not found.  
*/  
public static void initDefaultLoader() throws AgentPackageNotFoundException {  
    if (DEFAULT_LOADER == null) {  
        synchronized (AgentClassLoader.class) {  
            if (DEFAULT_LOADER == null) {  
                DEFAULT_LOADER = new AgentClassLoader(PluginBootstrap.class.getClassLoader());  
            }  
        }  
    }  
}  
  
public AgentClassLoader(ClassLoader parent) throws AgentPackageNotFoundException {  
    super(parent);  
    // SkyWalking agent.jar 文件的相对路径,该路径是必需的,用于定位插件和工具
    File agentDictionary = AgentPackagePath.getPath();  
    classpath = new LinkedList<>();  
    // Config.Plugin.MOUNT 挂载插件文件夹。文件夹路径是相对于agent.jar的
    // Config.Plugin.MOUNT 的定义:public static List<String> MOUNT = Arrays.asList("plugins", "activations")
    Config.Plugin.MOUNT.forEach(mountFolder -> classpath.add(new File(agentDictionary, mountFolder)));  
}

插件定义体系

image.png

插件定义

拦截实例方法/构造器

抽象类 ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine,子类

public abstract class ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {  
  
    /**  
    * @return null, means enhance no static methods.  
    */  
    @Override  
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {  
        return null;  
    }  
 
}

拦截静态方法

ClassStaticMethodsEnhancePluginDefine

插件定义

AbstractClassEnhancePluginDefine 是所有插件定义的顶级父类

  1. 要拦截的类:enhanceClass()
  2. 要拦截的方法:getXxxInterceptPoints()

目标类匹配

ClassMatch

  1. 按类名匹配:NameMatch
  2. 间接匹配:IndirectMatch
    2.1. PrefixMatch
    2.2. MethodAnnotationMatch

拦截器定义

  1. beforeMethod
  2. afterMethod
  3. handleMethodException

插件声明

插件声明的文件:resources/skywalking-plugin.def
插件名称 = 插件定义

插件加载

image.png

实例化所有插件

PluginBootstrap

  1. PluginResourcesResolver 查找 skywalking-plugin.def
  2. PluginCfg 封装 PluginDefine
  3. DynamicPluginLoader 加载基于 xml 配置的插件

分类插件

PluginFinder

  1. 命名插件-NameMatch
  2. 间接匹配插件-IndirectMatch
  3. JDK 类库插件