准备
1、下载skywalking的开源代码到本地,根据文档进行编译:
Prepare JDK8+ and Maven 3.6+
Run ./mvnw clean package -DskipTests
All packages are in /dist.(.tar.gz for Linux and .zip for Windows).
maven自动下载包和插件。
2、下载插件translation可以翻译英文注释。
3、找到启动文件,ApplicationStartUp,在apm-webapp文件下,是此项目的启动文件。
4、入口类 org.skywalking.apm.agent.SkyWalkingAgent
1、apm-sniffer包下的apm-agent包中的skywalkingagent类的premain方法为入口:
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
......
}
(1)agentArgs 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入。
(2)instrumentation 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。
java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这个包的核心部分,
集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。
skywalking利用了javaagent实现了虚拟机级别的aop,利用字节码工具bytebuddy实现字节码增强。
2、初始化:
SnifferConfigInitializer.initializeCoreConfig(agentArgs); //读取配置文件信息,环境变量信息,vm option参数
3、加载插件:
skywalking通过插件加载机制,实现对不同中间件等的监控,即加载对应的插件即可
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
在PluginBootstrap类中的loadPlugins()中:
//先声明一个资源加载器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()来判断需要修改哪些类的字节码(因为不是每一个类都需要增强):
//定义了集合
//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中,每一个插件必须去实现这个类中的方法
ClassMatch match = plugin.enhanceClass(); //故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);
}
以dubbo插件为例:
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;
}
}
};
}
@Override
protected ClassMatch enhanceClass() { //重写了基类的enhanceClass()方法,指定了需要增强的类
return byName("com.alibaba.dubbo.common.bytecode.Wrapper");
}
}
4、通过bytebuddy中的agentBuilder来生成一个agent,并执行相关逻辑:
利用bytebuddy的API生成一个代理,并执行transform方法和监听器Listener(主要是日志相关)。
在premain中,通过链式调用,被builderMatch()匹配到的类都会执行transform方法,transform定义了字节码增强的逻辑:
agentBuilder.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new Listener())
.installOn(instrumentation);
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( //最重要的define方法,在AbstractClassEnhancePluginDefine中定义
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 实例方法,它主要做了下面的几次事 ( 入口方法)
1 找到增强插件类的所有显式指定的 WitnessClasses - 用户自己重载显式指定
2 调用 enhance 方法, 执行真正的插件的增强逻辑,返回新的 DynamicType.Builder
3 设置上下文, 标记初始化定义步骤已完成
4 最后再返回新的 第 2 中的增强后的 newClassBuilder
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);
/**
* find witness classes for enhance class
*/
String[] witnessClasses = witnessClasses();
if (witnessClasses != null) {
for (String witnessClass : witnessClasses) {
if (!WitnessClassFinder.INSTANCE.exist(witnessClass, classLoader)) {
LOGGER.warn("enhance class {} by plugin {} is not working. Because witness class {} is not existed.", transformClassName, interceptorDefineClassName, witnessClass);
return null;
}
}
}
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[] {};
}
public boolean isBootstrapInstrumentation() {
return false;
}
public abstract ConstructorInterceptPoint[] getConstructorsInterceptPoints();
public abstract InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints();
public abstract StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints();
}
这个抽象基类中,最为重要的一个方法 define(),被premain中的transform调用,从而实现插件中的拦截逻辑。
5、启动服务
skywalking采用插件架构,为了规范所有插件,插件都必须实现BootService这个接口,如下:
//BootService中声明的生命周期方法,agent-core内核会在适当的时机调用不同的生命周期方法
public interface BootService {
void prepare() throws Throwable;
void boot() throws Throwable;
void onComplete() throws Throwable;
void shutdown() throws Throwable;
}
在premain方法的最后,会boot启动服务:
try {
ServiceManager.INSTANCE.boot();
} catch (Exception e) {
LOGGER.error(e, "Skywalking agent boot failure.");
}
boot类是ServiceManager类下的方法,打开boot方法:
public void boot() {
bootedServices = loadAllServices();
prepare();
startup();
onComplete();
}
<1>首先是loadAllService方法:
private Map<Class, BootService> loadAllServices() {
Map<Class, BootService> bootedServices = new LinkedHashMap<>();
List<BootService> allServices = new LinkedList<>();
load(allServices); //load方法,采用SPI机制实现
for (final BootService bootService : allServices) {
Class<? extends BootService> bootServiceClass = bootService.getClass();
//服务是否有注解@DefaultImplementor,默认实现
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;
}
void load(List<BootService> allServices) { //load方法, ServiceLoader.load(BootService.class, AgentClassLoader.getDefault()))是典型的SPI机制
for (final BootService bootService : ServiceLoader.load(BootService.class, AgentClassLoader.getDefault())) {
allServices.add(bootService);
}
}
扩展:Java SPI思想梳理 - KKys的文章 - 知乎 https://zhuanlan.zhihu.com/p/28909673
6、最后一 步,注册关闭钩子
设置一个关闭钩子,在JVM关闭时,建立一个线程,调用ServiceManager中的shutdown方法,进行资源释放等。
Runtime.getRuntime()
.addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread"));