可以看手写skywalking查找插件的代码
https://www.bilibili.com/video/BV1Jv4y1a7Kw?p=20&vd_source=d9a52d82a1f11ceeeb7021c93269e8d2
源码核心是SkyWalkingAgent
找到一堆插件,来对符合条件的类来代理
通过AbstractClassEnhancePluginDefine.define方法来。
如果有很多版本的插件,spring有2.0版本,3.0版本,4.0版。
具体使用哪个版本,看被增加的类使用的是哪个版本的spring
比如
- spring 2有(A类,B类,C类)
- spring3 有(B类,C类,D类)
- spring4有 (B类,C类,D类)
如果应用程序使用的spring2,那么一定会有A类,那skywalking会选择spring2插件匹配,如果应用程序使用的spring4,由于spring3和spring4的类都相同,那么在比较spring3和spring4中的方法。
找出各种独有的方法来匹配,然后在确定使用哪个版本的插件
以拦截实例方法为例ClassEnhancePluginDefine.enhanceInstance
拦截到的方法,会交给InstMethodsInter来处理。像aop一样。
package com.test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@RequestMapping("/hello")
public Object test(){
return "hello";
}
}
通过arthas 反编译的代码
package com.test;
import com.test.IndexController;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ConstructorInter;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController
implements EnhancedInstance {
private volatile Object _$EnhancedClassField_ws;
public static volatile /* synthetic */ InstMethodsInter delegate$frmhjm0;
public static volatile /* synthetic */ InstMethodsInter delegate$iuour40;
public static volatile /* synthetic */ ConstructorInter delegate$50sob50;
private static final /* synthetic */ Method cachedValue$L2xuREhn$gvchht3;
public static volatile /* synthetic */ InstMethodsInter delegate$acsh691;
public static volatile /* synthetic */ InstMethodsInter delegate$qc4vlq1;
public static volatile /* synthetic */ ConstructorInter delegate$vm2r3i1;
private static final /* synthetic */ Method cachedValue$ImSA9xmx$gvchht3;
public IndexController() {
this(null);
delegate$vm2r3i1.intercept(this, new Object[0]);
}
private /* synthetic */ IndexController(auxiliary.74kq0FAk kq0FAk) {
}
@RequestMapping(value={"/hello"})
public Object test() {
return delegate$qc4vlq1.intercept(this, new Object[0], (Callable<?>)new auxiliary.0hTUYoEs(this), cachedValue$ImSA9xmx$gvchht3);
}
private /* synthetic */ Object test$original$bo9sTfYx() {
/*15*/ return "hello";
}
static {
ClassLoader.getSystemClassLoader().loadClass("org.apache.skywalking.apm.dependencies.net.bytebuddy.dynamic.Nexus").getMethod("initialize", Class.class, Integer.TYPE).invoke(null, IndexController.class, 1905055592);
cachedValue$ImSA9xmx$gvchht3 = IndexController.class.getMethod("test", new Class[0]);
}
final /* synthetic */ Object test$original$bo9sTfYx$accessor$ImSA9xmx() {
return this.test$original$bo9sTfYx();
}
}
有上面的源码可以看出,对test方法进行了增强,内部其实是调用了delegate$qc4vlq1的intercept方法,就是InstMethodsInter中aop相关逻辑
会执行InstanceMethodsAroundInterceptor接口的3个方法。
比如上面IndexController这个类,因为有@RestController 注解,所以就被对应的RestControllerInstrumentation给匹配上了
增强后的代码会交个org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.RequestMappingMethodInterceptor来处理
可以看到,当我们调用http://localhost:8088/hello 这个接口时,就被拦截到了,然后调用RequestMappingMethodInterceptor的beforeMethod方法进行处理
以duboo插件为类DubboInterceptor的拦截器实现了InstanceMethodsAroundInterceptor
SPAN
span 记录
1.EntrySpan
springboot项目 当一个请求到达ctroller之前,会先tomcat==>spring mvc 然后在到达业务
1.在到达tomcat的时候,会先创建一个EntrySpan。同时记录下Tags和SpanLayer的信息。
org.apache.skywalking.apm.plugin.tomcat78x.TomcatInvokeInterceptor
peek 这个方法其实是一个类似栈的数据结构activeSpanStack,先判断下activeSpanStack有没有数据,没有数据的说明,tomcat是一个入口,创建了EntrySpan后,放到activeSpanStack中
2.进入到springMVC后,因为不知道有没有创建EntrySpan ,所以又进行了创建
org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.AbstractMethodInterceptor#beforeMethod
由于tomcat已经创建了entrySpan,所以没有创建新的,同时spring MVC的layer,tags和logs会覆盖tomcat设置的值,
也就是说EntrySpan记录的信息最靠近服务提供测的信息
ExitSpan 和LocalSpan
请求进来是从tomcat------>spring mvc—>getUser ,EntrySpan是tomcat创建,springmvc来复用
请求返回是 从getUser—>spring mvc---->tomcat 这个时候是插件嵌套,springMVC创建ExitSpan,tomcat来复用
比如上图中getUser方法,需要调用redis和Mysql,这个时候就不在同一个服务或者线程了,需要创建二个ExitSpan
LocalSpan 可以用来记录本地的一些方法,比如上图的getUser----->checkUser
(1) tomcat 创建EntrySpan
(2)spring mvc复用EntrySpan
(3)org.apache.skywalking.apm.plugin.lettuce.v5.AsyncCommandMethodInterceptor#beforeMethod
创建LocalSpan
写了本地的方法好像没有创建LocalSpan
(4) redis 创建ExitSpan
org.apache.skywalking.apm.plugin.lettuce.v5.RedisChannelWriterInterceptor#beforeMethod