最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

世界速递!skywalking插件工作原理剖析

来源:博客园

1. 官方插件二次开发

前面在介绍skywalking-agent目录时,提到了它有一个插件目录,并支持动态的开发插件。其实skywalking默认已经提供大部分框架的插件了,一般情况下不需要额外开发新的插件,可以直接改造已有的插件,使其适配自己的业务。


(相关资料图)

下面介绍如何二次开发SpringMVC插件以采集业务参数。

(1)下载插件源码

在skywalking 8.7.0及以前的版本,插件的源码是直接放在skywalking主项目中的。在8.7.0以后的版本,把插件移出去了。(这一点好坑,我在skywalking最新版本里找了好久没找到插件的源码)。

下面分析的源码来自于skywalking 8.7.0版本。

  • URL:https://github.com/apache/skywalking/tree/v8.7.0

  • 插件源码位置:skywalking/apm-sniffer/apm-sdk-plugin(下面有个spring-plugins目录,里面就是spring相关的插件)

由于我的项目中用的SpringMVC是5.x的版本,因此主要关注下图中标记的两个目录。

顺便提一句skywalking 8.7.0以后版本的源码:- URL:https://github.com/apache/skywalking-java- 插件源码位置:skywalking-java/apm-sniffer/apm-sdk-plugin

(2)由SpringMVC插件窥探整个框架

① SpringMVC插件模块一览

一眼看过去,眼前一黑。仔细看就会发现,这里面的类主要分两大类,以InstrumentationInterceptor结尾。Interceptor结尾的一般是拦截器,熟悉java agent技术的同学一般会知道Instrumentation,这个其实就是agent技术中的核心类,它可以加载Class文件,甚至可以修改Class文件。

我们发现有两个类名比较熟悉:ControllerInstrumentationRestControllerInstrumentationControllerRestController就是SpringMVC中常用的两个注解,本项目中使用的是RestController注解,就以此类为入口吧。

这里不对java agent对额外的说明了,想要看懂skywalking框架,必须要先了解java agent。

② RestControllerInstrumentation类源码

public class RestControllerInstrumentation extends AbstractControllerInstrumentation {    // 这玩意就是RestController注解    public static final String ENHANCE_ANNOTATION = "org.springframework.web.bind.annotation.RestController";        @Override    protected String[] getEnhanceAnnotations() {        return new String[] {ENHANCE_ANNOTATION};    }}

可以发现,ENHANCE_ANNOTATION这个属性的值就是SpringMVC中那个注解类。enhance是增强的意思,很好理解,这里就是要增强RestController注解的功能。看下getEnhanceAnnotations()方法在哪里被调用了。

③ AbstractControllerInstrumentation类源码

AbstractControllerInstrumentation类是RestControllerInstrumentation的父类,在enhanceClass()方法中调用了getEnhanceAnnotations()方法。这样就串起来了,这里实际上就是增强了被RestController注解修饰的类的功能。那么到底是怎么增强的呢?

public abstract class AbstractControllerInstrumentation extends AbstractSpring5Instrumentation {    @Override    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {// 忽略}            @Override    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {        // 这里声明了方法切入点,返回的是一个数组,说明可以有多个切入点        return new InstanceMethodsInterceptPoint[] {            new DeclaredInstanceMethodsInterceptPoint() {                @Override                public ElementMatcher getMethodsMatcher() {                    // 这里是被RequestMapping注解的方法作为一个切入点                    return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.RequestMapping"));                }                @Override                public String getMethodsInterceptor() {                    // 这里是此切入点的拦截器                    return Constants.REQUEST_MAPPING_METHOD_INTERCEPTOR;                }                @Override                public boolean isOverrideArgs() {                    return false;                }            },            new DeclaredInstanceMethodsInterceptPoint() {                @Override                public ElementMatcher getMethodsMatcher() {                    // 这里是被GetMapping、PostMapping等注解的方法作为一个切入点                    return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.GetMapping"))                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PostMapping")))                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PutMapping")))                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.DeleteMapping")))                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PatchMapping")));                }                @Override                public String getMethodsInterceptor() {                    // 这里是此切入点的拦截器                    return Constants.REST_MAPPING_METHOD_INTERCEPTOR;                }                @Override                public boolean isOverrideArgs() {                    return false;                }            }        };    }        @Override    protected ClassMatch enhanceClass() {        // 这里是是增强类的功能        return ClassAnnotationMatch.byClassAnnotationMatch(getEnhanceAnnotations());    }    protected abstract String[] getEnhanceAnnotations();}
public class Constants {    public static final String REQUEST_MAPPING_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.RequestMappingMethodInterceptor";    public static final String REST_MAPPING_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.RestMappingMethodInterceptor";}

getInstanceMethodsInterceptPoints()方法中,可以看到很多熟悉的注解,比如PostMapping。结合一下方法名,可以大胆的推测,这里其实就是定义对象实例方法的拦截切入点。再看下getMethodsInterceptor()方法中使用的两个常量,以REST_MAPPING_METHOD_INTERCEPTOR为例,它的值就是个类名:RestMappingMethodInterceptor,那这个类肯定就是负责增强功能的拦截器。进入这个拦截器瞧瞧。

④ RestMappingMethodInterceptor类源码

// 这里省略了大量的代码public class RestMappingMethodInterceptor extends AbstractMethodInterceptor {    @Override    public String getRequestURL(Method method) {        // 这里是从注解中解析请求url        return ParsePathUtil.recursiveParseMethodAnnotation(method, m -> {            String requestURL = null;            GetMapping getMapping = AnnotationUtils.getAnnotation(m, GetMapping.class);            if (getMapping != null) {                if (getMapping.value().length > 0) {                    requestURL = getMapping.value()[0];                } else if (getMapping.path().length > 0) {                    requestURL = getMapping.path()[0];                }            }            return requestURL;        });    }    @Override    public String getAcceptedMethodTypes(Method method) {        // 这里是从注解中解析请求类型        return ParsePathUtil.recursiveParseMethodAnnotation(method, m -> {            if (AnnotationUtils.getAnnotation(m, GetMapping.class) != null) {                return "{GET}";            } else {                return null;            }        });    }}

这个类里的两个方法都是工具方法,仅仅解析了请求url和类型。其他的功能实现肯定在它的父类AbstractMethodInterceptor里。

⑤ AbstractMethodInterceptor类源码

// 这里省略了大量的代码,只保留了核心的部分public abstract class AbstractMethodInterceptor implements InstanceMethodsAroundInterceptor {    public abstract String getRequestURL(Method method);    public abstract String getAcceptedMethodTypes(Method method);    @Override    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes,                             MethodInterceptResult result) throws Throwable {        // 这是在被切入的方法执行前,要执行的逻辑        Object request = ContextManager.getRuntimeContext().get(REQUEST_KEY_IN_RUNTIME_CONTEXT);        if (request != null) {            StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH);            if (stackDepth == null) {                final ContextCarrier contextCarrier = new ContextCarrier();                // 如果请求类是继承自servlet-api提供的HttpServletRequest类,则走此方法                if (IN_SERVLET_CONTAINER && HttpServletRequest.class.isAssignableFrom(request.getClass())) {                    final HttpServletRequest httpServletRequest = (HttpServletRequest) request;                    // AbstractSpan是skywalking中日志的一个载体,用于采集数据                    AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);                    // 采集请求URL                    Tags.URL.set(span, httpServletRequest.getRequestURL().toString());                    // 采集请求的类型,如GET、POST                    Tags.HTTP.METHOD.set(span, httpServletRequest.getMethod());                    // 标记是SpringMVC的日志                    span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION);                    // 标记是HTTP请求                    SpanLayer.asHttp(span);                    if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) {                        // 采集请求参数                        RequestUtil.collectHttpParam(httpServletRequest, span);                    }                    if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) {                        // 采集请求头                        RequestUtil.collectHttpHeaders(httpServletRequest, span);                    }                } else {                    throw new IllegalStateException("this line should not be reached");                }                stackDepth = new StackDepth();                ContextManager.getRuntimeContext().put(CONTROLLER_METHOD_STACK_DEPTH, stackDepth);            }            stackDepth.increment();        }    }    @Override    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes,                              Object ret) throws Throwable {        // 这是在被切入的方法执行后,要执行的逻辑    }}

这个类中就是增强的功能,可以看出它能在被切入的方法之前和之后增强功能,在被请求注解类(如PostMapping)注解的方法执行前,它可以采集到请求的url、请求头、请求内容等等。那么在方法执行后,它肯定也可以采集到响应内容。

⑥ 小结

看到这里,SpringMVC插件采集的原理基本上可以猜到了,就是实现了拦截器,拦截了被注解的请求接口方法,并在方法执行前后采集数据。

且先不管这个拦截器到底是怎么工作的,看到这里,基本上已经明了该如何采集自定义的业务数据了,就是直接修改AbstractMethodInterceptor源码即可。

(3)业务请求参数采集

① 默认的请求参数采集

if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) {    // 采集请求参数    RequestUtil.collectHttpParam(serverHttpRequest, span);}

看到采集参数的条件是COLLECT_HTTP_PARAMS,点进去看,好像是个配置类,默认是关闭的,那可以先把它打开。

public class SpringMVCPluginConfig {    public static class Plugin {        @PluginConfig(root = SpringMVCPluginConfig.class)        public static class SpringMVC {            // 是否采集请求参数的开关,默认是关闭的            public static boolean COLLECT_HTTP_PARAMS = false;        }    }}

skywalking-agent/config/agent.config配置文件中搜一下,果然找到了:

# 采集SpringMVC请求参数的开关plugin.springmvc.collect_http_params=${SW_PLUGIN_SPRINGMVC_COLLECT_HTTP_PARAMS:false}

然后再看采集的方法RequestUtil.collectHttpParam(serverHttpRequest, span)

public class RequestUtil {    public static void collectHttpParam(HttpServletRequest request, AbstractSpan span) {        // 获取请求参数        final Map parameterMap = request.getParameterMap();        if (parameterMap != null && !parameterMap.isEmpty()) {            String tagValue = CollectionUtil.toString(parameterMap);            tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ?                    StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;            // 将请求参数写入日志http.params字段中            Tags.HTTP.PARAMS.set(span, tagValue);        }    }}

可以看到,获取请求参数是用的request.getParameterMap()方法,如果你的接口使用的表单方式提交,那么恭喜,参数可以被采集起来。如果使用application/json协议提交参数,不好意思,它采集不到。我的项目里都是后者,因此要额外开发。

② 自定义业务参数采集

使用application/json协议提交的参数,是不方便从request中直接解析出来的。而这里的Object[] allArguments是切入点方法的入参,刚好就是请求参数,因此这里就直接利用其解析请求参数。

if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) {    if (!StringUtils.isEmpty(httpServletRequest.getContentType())        && httpServletRequest.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {        // 采集使用application/json协议提交的参数        recordJsonReqLog(allArguments, span, httpServletRequest.getHeader("X_TRACEID"));    } else {        RequestUtil.collectHttpParam(httpServletRequest, span);    }}// 记录业务参数的方法private void recordJsonReqLog(Object[] allArguments, AbstractSpan span, String traceId) {    // 记录业务的调用链ID    if (!StringUtils.isEmpty(traceId)) {        span.tag(new StringTag("traceId"), traceId);    }    if (allArguments != null && allArguments.length > 0) {        // 记录请求参数        String param = GSON.toJson(allArguments[0]);        span.tag(new StringTag("http.req"), param);        try {            // 解析请求参数            JsonObject jsonObject = GSON.fromJson(param, JsonObject.class);            JsonObject data = jsonObject.getAsJsonObject("data");            if (data == null) {                // 如果没有data参数,直接解析外层参数                data = jsonObject;            }            // 记录业务参数            Optional.ofNullable(data.get("account")).ifPresent(jsonElement                     -> span.tag(new StringTag("account"), jsonElement.getAsString()));            Optional.ofNullable(data.get("userId")).ifPresent(jsonElement                    -> span.tag(new StringTag("userId"), jsonElement.getAsString()));            Optional.ofNullable(data.get("deviceId")).ifPresent(jsonElement                    -> span.tag(new StringTag("deviceId"), jsonElement.getAsString()));        } catch (JsonSyntaxException e) {        }    }}

采集响应参数的思路和上面一样,这里不做介绍了。

(4)使用插件

上面修改的是AbstractMethodInterceptor类,这个类所在模块为apm-springmvc-annotation-commons,所以直接使用maven命令打包,将生成的产物apm-springmvc-annotation-commons-8.7.0.jar拷贝到skywalking-agent/plugins目录下,然后重新构建项目并部署,即可生效。

(5)小结

看到这里,基本上我们已经学会skywalking的一些高级用法了,可以做一些简单的插件二次开发,足以应对项目中大部分业务数据的采集。其他的插件和SpringMVC插件类似,代码的结构基本上差不多。

2. 插件原理剖析

上面的SpringMVC插件源码,我们跟到拦截器Interceptor,就停止了,那么拦截器到底是如何加载的呢?值得好好研究研究。(不得不说,skywalkig的源码写得是真的很复杂,但是确实很牛B。)

在研究之前,我要先介绍一下Byte Buddy

(1)Byte Buddy介绍

runtime code generation for the Java virtual machine

上面这段话摘自github上该项目的简介,翻译过来就是针对JVM虚拟机的运行时代码生成。这不就是动态代理么?话不多说,先上一段代码:

// agent探针类public class ToStringAgent {    // 探针入口    public static void premain(String arguments, Instrumentation instrumentation) {        // AgentBuilder是Byte Buddy中的一个构建器        new AgentBuilder.Default()            // 拦截被ToString注解的类            .type(isAnnotatedWith(ToString.class))            // 被拦截的类要增强的功能            .transform(new AgentBuilder.Transformer() {                // DynamicType.Builder是Byte Buddy中用于生成代码的一个重要的构建器                @Override                public DynamicType.Builder transform(DynamicType.Builder builder,                                                      TypeDescription typeDescription,                                                     ClassLoader classloader) {                    // 拦截名称为toString的方法                    return builder.method(ElementMatchers.named("toString"))                        // 使用ToStringInterceptor拦截器增强toString方法的功能                        .intercept(MethodDelegation.to(ToStringInterceptor.class));                }            }).installOn(instrumentation);    }}
// 拦截器public class ToStringInterceptor {    // RuntimeType是Byte Buddy中应用于拦截器的注解,被它注解的方法就是运行时拦截器要执行的目标方法    @RuntimeType    // Origin注解的就是被代理的目标方法,AllArguments注解的是被代理的目标方法的参数,SuperCall注解的是被代理方法的调用器    public static Object intercept(@Origin Method method, @AllArguments Object[] args, @SuperCall Callable callable)         throws Exception {        System.out.println("被代理的方法执行前,执行了拦截器");        try {            // 执行被代理的方法            return callable.call();        } finally {            System.out.println("被代理的方法执行后,执行了拦截器");        }    }}

这段代码,要使用java agent技术来应用到项目中。它实现的功能就是,拦截项目中被ToString注解的类中的toString()方法,拦截器可以拿到目标方法执行时的所有信息,包括方法的入参,并且在目标方法执行前后,执行一段额外的逻辑。

看这实现的功能是不是有点眼熟,这不就是Lombok中注解实现的功能么?

(2)skywalking插件执行原理

在1-2节中,我们跟源码跟到了AbstractMethodInterceptor类,继续从这个类入手。

// 这里省略了大量的代码,只保留了核心的部分public abstract class AbstractMethodInterceptor implements InstanceMethodsAroundInterceptor {    @Override    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes,                             MethodInterceptResult result) throws Throwable {        // 这是在被切入的方法执行前,要执行的逻辑    }    @Override    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes,                              Object ret) throws Throwable {        // 这是在被切入的方法执行后,要执行的逻辑    }}

① beforeMethod()方法在哪里调用的?

这里有好几个类都调用了beforeMethod()方法,这里我直接揭晓答案了,SpringMVC的拦截器走的是InstMethodsInter。下面那个带OverrideArgs的类,和1-2节中AbstractControllerInstrumentation类中构建拦截器切入点中的isOverrideArgs()方法应该有关,由于SpringMVC拦截器该方法返回的是false,因此不看这个类。

② 核心拦截器InstMethodsInter

public class InstMethodsInter {    private static final ILog LOGGER = LogManager.getLogger(InstMethodsInter.class);    private InstanceMethodsAroundInterceptor interceptor;        // 构造方法    public InstMethodsInter(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {        try {            // 使用类加载器加载拦截器到JVM中。这里要加载的原因是,插件拦截器都在独立的jar包,不在agent主程序里            interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);        } catch (Throwable t) {            throw new PluginException("Can"t create InstanceMethodsAroundInterceptor.", t);        }    }        // 拦截器的主方法,看到RuntimeType、AllArguments这些注解没,都是Byte Buddy里的    @RuntimeType    public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable zuper,        @Origin Method method) throws Throwable {        EnhancedInstance targetObject = (EnhancedInstance) obj;        MethodInterceptResult result = new MethodInterceptResult();        try {            // 拦截器在代理方法执行前执行beforeMethod方法            interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);        } catch (Throwable t) {            LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());        }        // 接收被代理的方法执行的结果        Object ret = null;        try {            if (!result.isContinue()) {                ret = result._ret();            } else {                // 执行被代理的方法                ret = zuper.call();            }        } catch (Throwable t) {            try {                // 拦截器捕获被代理的方法的异常                interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);            } catch (Throwable t2) {                LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());            }            throw t;        } finally {            try {                // 拦截器在代理方法执行后执行afterMethod方法                ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);            } catch (Throwable t) {                LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());            }        }        return ret;    }}

看到@RuntimeType注解就松了一口气了,这就是Byte Buddy中拦截器的写法。

③ 核心拦截器在哪里用的?

直接点击InstMethodsInter类,看它在哪里用到了:

// 这里仅保留了核心代码public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePluginDefine {    private static final ILog LOGGER = LogManager.getLogger(ClassEnhancePluginDefine.class);    // 增强类实例的核心方法    protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription,        DynamicType.Builder newClassBuilder, ClassLoader classLoader,        EnhanceContext context) throws PluginException {        // 获取实例类方法拦截切入点        InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();        boolean existedMethodsInterceptPoints = false;        if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {            existedMethodsInterceptPoints = true;        }        if (existedConstructorInterceptPoint) {            // 增强构造方法        }        if (existedMethodsInterceptPoints) {            // 增强实例类方法            for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {                // 判断是否属于要拦截的目标方法的条件                ElementMatcher.Junction junction = not(isStatic())                    // getMethodsMatcher()在上面1-2节也提到了                    .and(instanceMethodsInterceptPoint.getMethodsMatcher());                // 这里就是Byte Buddy中构建方法拦截器的核心写法。                newClassBuilder = newClassBuilder.method(junction)                    // 设置拦截器                    .intercept(MethodDelegation.withDefaultConfiguration()                               .to(new InstMethodsInter(interceptor, classLoader)));            }        }        return newClassBuilder;    }}

这个类中可以看出,它将拦截器与被拦截的类编织到一起了。继续跟踪enhanceInstance()方法在哪里调用,一路找过去,最终到了Transformer类。

④ SkyWalkingAgent探针的入口

最终跟到了SkyWalkingAgent.Transformer类,看到这里,基本上就明白了:

public class SkyWalkingAgent {    private static ILog LOGGER = LogManager.getLogger(SkyWalkingAgent.class);        // Agent探针的入口    public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {        // 加载插件        final PluginFinder pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());        // Byte Buddy的构建器        AgentBuilder agentBuilder = new AgentBuilder...;        // type()判断是否需要由插件拦截        agentBuilder.type(pluginFinder.buildMatch())            // 这里就是设置拦截器            .transform(new Transformer(pluginFinder))            .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)            .with(new RedefinitionListener())            .with(new Listener())            .installOn(instrumentation);    }    // Byte Buddy的拦截器    private static class Transformer implements AgentBuilder.Transformer {        @Override        public DynamicType.Builder transform(final DynamicType.Builder builder,                                                final TypeDescription typeDescription,                                                final ClassLoader classLoader,                                                final JavaModule module) {            LoadedLibraryCollector.registerURLClassLoader(classLoader);            // 取出插件            List pluginDefines = pluginFinder.find(typeDescription);            if (pluginDefines.size() > 0) {                // Byte Buddy中用于生成代码的一个重要的构建器                DynamicType.Builder newBuilder = builder;                EnhanceContext context = new EnhanceContext();                // 遍历插件                for (AbstractClassEnhancePluginDefine define : pluginDefines) {                    // 在这里加载插件中的拦截器,最终调用了上面提到的ClassEnhancePluginDefine.enhanceInstance()方法                    DynamicType.Builder possibleNewBuilder = define.define(                        typeDescription, newBuilder, classLoader, context);                    if (possibleNewBuilder != null) {                        newBuilder = possibleNewBuilder;                    }                }                return newBuilder;            }            return builder;        }    }}

在skywalking探针的入口方法中,就已经加载了所有插件的拦截器,并将拦截器和需要拦截的方法关联到了一起,剩下的功能,就交给Byte Buddy了。

(3)小结

看到这里,skywalking探针工作的原理,就已经清楚了。skywalking是利用了java agentByte Buddy两项技术,来实现无侵入式的拦截。如果不了解这两项技术,直接看源码会一脸懵B。

关键词: