™技术博客

apm | agent字节码增强实现

2021年6月13日

Agent源码解析

入口类

在 apm-sniffer/apm-agent 包下:
com.ccb.aicloud.apm.agent.ApmAgent#premain

1
2
3
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
... ...
}
  • agentArgs: 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入
  • instrumentation: 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入

利用了javaagent实现了虚拟机级别的aop,利用字节码工具bytebuddy实现字节码增强

初始化

读取配置文件信息,环境变量信息,vm option参数
SnifferConfigInitializer.initializeCoreConfig(agentArgs);

加载插件

通过插件加载机制,实现对不同中间件等的监控,即加载对应的插件即可
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
在PluginBootstrap类中的loadPlugins()中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//先声明一个资源加载器resolver,在resolver中的getResources()会查找skywalking-plugin.def文件中定义的类
PluginResourcesResolver resolver = new PluginResourcesResolver();
List<URL> resources = resolver.getResources();

//循环load 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的类,保存到list中
List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();

List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();

//通过反射创建这些类的实例
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
return plugins;

将返回的plugins传给pluginFinder类,作为构造参数,pluginFinder类中有方法buildMatch()来判断需要修改哪些类的字节码(因为不是每一个类都需要增强):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

//定义了集合
//pluginFinder将插件分类保存在两个集合中,分别是:按名字分类和按其他辅助信息分类
private final Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<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) {
//抽象方法enhanceClass方法定义在插件的抽象基类AbstractClassEnhancePluginDefine中,每一个插件必须去实现这个类中的方法
//故enhanceClass是每个插件都会自己去实现的方法,指定需要增强的类
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);
}
}
}

//typeDescription是bytebuddy的内置接口,是对类的完整描述,包含了类的全类名
//传入typeDescription,返回可以运用于typeDescription的类的插件
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));
}
//通过除了名字之外的辅助信息,在signatureMatchDefine集合中查找
for (AbstractClassEnhancePluginDefine pluginDefine : signatureMatchDefine) {
IndirectMatch match = (IndirectMatch) pluginDefine.enhanceClass();
if (match.isMatch(typeDescription)) {
matchedPlugins.add(pluginDefine);
}
}

return matchedPlugins;
}

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

public List<AbstractClassEnhancePluginDefine> getBootstrapClassMatchDefine() {
return bootstrapClassMatchDefine;
}

以dubbo插件为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//间接继承了基类
public class WrapperInstrumentation extends ClassStaticMethodsEnhancePluginDefine {
@Override
public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
return new StaticMethodsInterceptPoint[] {
new StaticMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("makeWrapper");
}

@Override
public String getMethodsInterceptor() {
return "org.apache.skywalking.apm.plugin.dubbo.patch.MakeWrapperInterceptor";
}

@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}

//重写了基类的enhanceClass()方法,指定了需要增强的类
@Override
protected ClassMatch enhanceClass() {
return byName("com.alibaba.dubbo.common.bytecode.Wrapper");
}
}

通过bytebuddy中的agentBuilder来生成一个agent,并执行相关逻辑

利用bytebuddy的API生成一个代理,并执行transform方法和监听器Listener(主要是日志相关)。
在premain中,通过链式调用,被builderMatch()匹配到的类都会执行transform方法,transform定义了字节码增强的逻辑:

1
2
3
4
5
agentBuilder.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new Listener())
.installOn(instrumentation);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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,
final ClassLoader classLoader,
final JavaModule module) {
//typeDescription是bytebuddy的内置接口,是对类的完整描述,包含了类的全类名
//这里返回的是所有可以运用于typeDescription的类的插件【一个类可能有多个插件,见def文件】
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;
}
}

需要补充的是:
AbstractClassEnhancePluginDefine 做为所有的插件都要继承实现的基础, 抽象类AbstractClassEnhancePluginDefine具体定义了什么行为,这些行为又有哪些意义?什么地方在调用这个方法?
AbstractClassEnhancePluginDefine中有四个方法

  • enhance抽象方法, 抽象类中无实现, 子类去实现,具体的增强的逻辑
  • enhanceClass抽象方法, 抽象类中无实现,子类去实现,具体的要增强的类的 Match
  • witnessClasses也是可以被重载的一个方法,子类可重载
  • define实例方法,它主要做了下面的几次事 ( 入口方法)
    • 找到增强插件类的所有显式指定的WitnessClasses - 用户自己重载显式指定
    • 调用 enhance 方法, 执行真正的插件的增强逻辑,返回新的DynamicType.Builder
    • 设置上下文, 标记初始化定义步骤已完成
    • 最后再返回新的 第 2 中的增强后的 newClassBuilder
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      public abstract class AbstractClassEnhancePluginDefine {
      private static final ILog LOGGER = LogManager.getLogger(AbstractClassEnhancePluginDefine.class);

      public DynamicType.Builder<?> define(TypeDescription typeDescription, DynamicType.Builder<?> builder,
      ClassLoader classLoader, EnhanceContext context) throws PluginException {
      String interceptorDefineClassName = this.getClass().getName();
      String transformClassName = typeDescription.getTypeName();
      if (StringUtil.isEmpty(transformClassName)) {
      LOGGER.warn("classname of being intercepted is not defined by {}.", interceptorDefineClassName);
      return null;
      }

      LOGGER.debug("prepare to enhance class {} by {}.", transformClassName, interceptorDefineClassName);
      WitnessFinder finder = WitnessFinder.INSTANCE;
      /**
      * find witness classes for enhance class
      */
      String[] witnessClasses = witnessClasses();
      if (witnessClasses != null) {
      for (String witnessClass : witnessClasses) {
      if (!finder.exist(witnessClass, classLoader)) {
      LOGGER.warn("enhance class {} by plugin {} is not working. Because witness class {} is not existed.", transformClassName, interceptorDefineClassName, witnessClass);
      return null;
      }
      }
      }
      List<WitnessMethod> witnessMethods = witnessMethods();
      if (!CollectionUtil.isEmpty(witnessMethods)) {
      for (WitnessMethod witnessMethod : witnessMethods) {
      if (!finder.exist(witnessMethod, classLoader)) {
      LOGGER.warn("enhance class {} by plugin {} is not working. Because witness method {} is not existed.", transformClassName, interceptorDefineClassName, witnessMethod);
      return null;
      }
      }
      }

      /**
      * find origin class source code for interceptor
      */
      DynamicType.Builder<?> newClassBuilder = this.enhance(typeDescription, builder, classLoader, context);

      context.initializationStageCompleted();
      LOGGER.debug("enhance class {} by {} completely.", transformClassName, interceptorDefineClassName);

      return newClassBuilder;
      }

      protected abstract DynamicType.Builder<?> enhance(TypeDescription typeDescription,
      DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader, EnhanceContext context) throws PluginException;

      protected abstract ClassMatch enhanceClass();

      protected String[] witnessClasses() {
      return new String[] {};
      }

      protected List<WitnessMethod> witnessMethods() {
      return null;
      }

      public boolean isBootstrapInstrumentation() {
      return false;
      }
      public abstract ConstructorInterceptPoint[] getConstructorsInterceptPoints();
      public abstract InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints();
      public abstract StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints();
      }
  • *这个抽象基类中,最为重要的一个方法 define(),被premain中的transform调用,从而实现插件中的拦截逻辑。**

启动服务

采用插件架构,为了规范所有插件,插件都必须实现BootService这个接口,如下:

1
2
3
4
5
6
7
8
9
10
//BootService中声明的生命周期方法,agent-core内核会在适当的时机调用不同的生命周期方法
public interface BootService {
void prepare() throws Throwable;

void boot() throws Throwable;

void onComplete() throws Throwable;

void shutdown() throws Throwable;
}

在premain方法的最后,会boot启动服务:

1
2
3
4
5
try {
ServiceManager.INSTANCE.boot();
} catch (Exception e) {
LOGGER.error(e, "Apm agent boot failure.");
}

boot类是ServiceManager类下的方法,打开boot方法:

1
2
3
4
5
6
7
public void boot() {
bootedServices = loadAllServices();

prepare();
startup();
onComplete();
}
  1. 首先是loadAllService方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    private Map<Class, BootService> loadAllServices() {
    Map<Class, BootService> bootedServices = new LinkedHashMap<>();
    List<BootService> allServices = new LinkedList<>();
    load(allServices);
    for (final BootService bootService : allServices) {
    Class<? extends BootService> bootServiceClass = bootService.getClass();
    boolean isDefaultImplementor = bootServiceClass.isAnnotationPresent(DefaultImplementor.class);
    if (isDefaultImplementor) {
    if (!bootedServices.containsKey(bootServiceClass)) {
    bootedServices.put(bootServiceClass, bootService);
    } else {
    //ignore the default service
    }
    } else {
    //服务是否有注解@OverrideImplementor,覆盖实现
    OverrideImplementor overrideImplementor = bootServiceClass.getAnnotation(OverrideImplementor.class);
    if (overrideImplementor == null) {
    if (!bootedServices.containsKey(bootServiceClass)) {
    bootedServices.put(bootServiceClass, bootService);
    } else {
    throw new ServiceConflictException("Duplicate service define for :" + bootServiceClass);
    }
    } else {
    Class<? extends BootService> targetService = overrideImplementor.value();
    if (bootedServices.containsKey(targetService)) {
    boolean presentDefault = bootedServices.get(targetService)
    .getClass()
    .isAnnotationPresent(DefaultImplementor.class);
    if (presentDefault) {
    bootedServices.put(targetService, bootService);
    } else {
    throw new ServiceConflictException(
    "Service " + bootServiceClass + " overrides conflict, " + "exist more than one service want to override :" + targetService);
    }
    } else {
    bootedServices.put(targetService, bootService);
    }
    }
    }

    }
    return bootedServices;
    }
    1
    2
    3
    4
    5
    void load(List<BootService> allServices) {
    for (final BootService bootService : ServiceLoader.load(BootService.class, AgentClassLoader.getDefault())) {
    allServices.add(bootService);
    }
    }

** 扩展:Java SPI思想梳理 **

最后一步 注册关闭钩子

设置一个关闭钩子,在JVM关闭时,建立一个线程,调用ServiceManager中的shutdown方法,进行资源释放等。

1
2
Runtime.getRuntime()
.addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "apm service shutdown thread"));
Tags: apm

扫描二维码,分享此文章