最新要闻
- 特斯拉Model 3起火殃及宝马新车 车主索赔法院如此判决
- 新疆阿瓦提长绒棉:清爽透气纯棉背心9.9元/件狂促
- 小伙戒指卡手遇130名消防员演训:正好现场教学 科普一定不能硬拽
- 每日热门:城会玩!印度法官无法判决向ChatGPT求助 专家称或成全球法院系统标配
- 专为小姐姐打造!雅迪冠能Q9发布:超级续航+超级好看
- 环球讯息:清明将至,我省各地倡导文明祭祀育新风—— 追思亲人,如何更好“重情”
- 世界速看:社保基金2022年四季度新进30股 增持64股
- 天天快看点丨法拉第未来发布会只展示了个车架 贾跃亭还有这些问题需要回答
- 女子带孩子吃饭故意往菜里扔头发 店家看监控发现:网友吐槽教坏孩子
- 号称德国500年纯酿造法!特斯拉推出啤酒GigaBier:3瓶卖667元
- 全球通讯!微软、索尼等巨头纷纷退出:全球最大游戏展E3宣布取消
- 当前速看:15.86万元起 新款大众途岳上市:换1.5T发动机、能喝92号油
- 让退化的森林再获新生
- 天天滚动:比亚迪不去美国 照样做大哥
- 世界观速讯丨海底捞2022年营收347亿!“最牛打工妹”杨利娟接任后扭亏为盈赚了13亿
- 天天热资讯!4G比5G更成熟 越来越多用户认可?三大运营商猛推5G 2025年将连接超10亿
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
世界速递!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插件模块一览
一眼看过去,眼前一黑。仔细看就会发现,这里面的类主要分两大类,以Instrumentation
和Interceptor
结尾。Interceptor结尾的一般是拦截器,熟悉java agent技术的同学一般会知道Instrumentation
,这个其实就是agent技术中的核心类,它可以加载Class文件,甚至可以修改Class文件。
我们发现有两个类名比较熟悉:ControllerInstrumentation
和RestControllerInstrumentation
,Controller
和RestController
就是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 agent
和Byte Buddy
两项技术,来实现无侵入式的拦截。如果不了解这两项技术,直接看源码会一脸懵B。
关键词:
-
全网最详细中英文ChatGPT-GPT-4示例文档-类比语句智能生成从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命
我们在写文章时,为了生动化一些事物,会经常使用类比的方法,将一类事物比喻成其它更加形象的事物,从...
来源: 世界速递!skywalking插件工作原理剖析
全网最详细中英文ChatGPT-GPT-4示例文档-类比语句智能生成从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命
全球观察:利用Jackson序列化实现数据脱敏
特斯拉Model 3起火殃及宝马新车 车主索赔法院如此判决
新疆阿瓦提长绒棉:清爽透气纯棉背心9.9元/件狂促
小伙戒指卡手遇130名消防员演训:正好现场教学 科普一定不能硬拽
每日热门:城会玩!印度法官无法判决向ChatGPT求助 专家称或成全球法院系统标配
专为小姐姐打造!雅迪冠能Q9发布:超级续航+超级好看
环球讯息:清明将至,我省各地倡导文明祭祀育新风—— 追思亲人,如何更好“重情”
当前消息!Microsoft Edge 分屏 推荐
国家统计局:3月份采购经理指数延续扩张走势
世界速看:社保基金2022年四季度新进30股 增持64股
天天快看点丨法拉第未来发布会只展示了个车架 贾跃亭还有这些问题需要回答
女子带孩子吃饭故意往菜里扔头发 店家看监控发现:网友吐槽教坏孩子
号称德国500年纯酿造法!特斯拉推出啤酒GigaBier:3瓶卖667元
全球通讯!微软、索尼等巨头纷纷退出:全球最大游戏展E3宣布取消
当前速看:15.86万元起 新款大众途岳上市:换1.5T发动机、能喝92号油
助力“科技+产业+金融”良性发展 上海首单非公开科创债发行
让退化的森林再获新生
天天滚动:比亚迪不去美国 照样做大哥
世界观速讯丨海底捞2022年营收347亿!“最牛打工妹”杨利娟接任后扭亏为盈赚了13亿
天天热资讯!4G比5G更成熟 越来越多用户认可?三大运营商猛推5G 2025年将连接超10亿
熔断和降级有什么区别?
资讯:国际金融市场早知道:3月31日
全球播报:上证报:美元拐点将至,分析师称人民币资产或有望成为避险资产
当前播报:冯小刚主演 中国版《忠犬八公》今日上映:日本版曾感动无数人
视讯!发布/上市时间来了!NV RTX 4070国行售价大曝光:玩家直呼老黄良心了
读SQL进阶教程笔记03_自连接
pcm音频的录制、播放及转换
全球观点:非洲致命疾病 出现症状24小时内死亡:医生直呼病毒可怕引外界担心
快报:媒体:谁在开发“一键脱衣”App 行为龌龊已触犯法律 应严惩开发者
环球热文:新疆新鑫矿业(03833.HK)2022年度纯利增44.4%至7.45亿元 末期息0.15元
【世界新视野】美国火车又双叒叕脱轨了:装乙醇车厢爆炸起火
SSD还要降价!今年是涨不回来了
全球消息!实测一个月 我发现了家用脱毛仪的真相
全球资讯:FF91量产了 不过市场里已经没了贾老板的位置
全球速递!2022年全球新能源销冠 每天进账11亿!“技术为王”的比亚迪做对了什么?
全球资讯:来月经能吃人参果吗?
“门口五不放,放了家运衰”,5种东西是啥?老祖宗的经验之谈
关注:01、关于满减优惠券可叠加使用场景下的动态规划算法
当前视点!Spring AOP官方文档学习笔记(一)之AOP概述
全球焦点!魅族20系列价格公布:2999元起、无界顶配8499元!
当前头条:Epic要免费送大作《消逝的光芒》增强版了:立省百元!网友:E宝YYDS
香蕉、蜂蜜治疗便秘?又骗我好多年
当前热议!俄妹COS《生化危机1》吉尔 战斗服难掩好身材
今头条!【kafka】-生产环境问题-报错Maximum application poll interval
热议:流畅、稳定全面进化:魅族Flyme 10正式发布
天天资讯:17999元 ROG新款XG Mobile显卡坞上架:175W移动版RTX 4090
天天看点:魅族Flyme Auto车机系统正式发布:交互完全不输华为问界
世界观点:满血标准版48个月不卡!魅族20发布:2999元起
每日观点:3999元起 魅族20 PRO发布:魅族史上最高标准旗舰
环球热资讯!React Router 备忘清单_开发速查表分享
每日观点:希尔排序
环球微动态丨强网杯2022 GameMaster
环球视讯!128只债券推迟或取消发行
全球新资讯:李强会见博鳌亚洲论坛理事会成员
当前焦点!一款在我国卖不好的手机:真实销量把苹果都拉下马了
非洲再现致命性疾病:出现症状24小时内死亡
每日看点!魅族20 INFINITY无界版由京东方供屏:采用四边等窄设计
最新:元气森林官方发车:0糖0脂气泡水+燃茶多口味饮料混合囤货 2.8元/瓶新低
煤油心目中的魅族王者归来 三年回到国内中高端市场TOP5
环球快资讯:游戏行业也衰退?EA拟裁员6%减少办公空间
环球观焦点:张译《他是谁》豆瓣评分两天再降0.2:刑侦局你给我讲爱情?
一箭四星!中国金牌火箭长二丁火箭又一次成功:31年来超高可靠性
环球热讯:距《你的名字》仅一步之遥:新海诚《铃芽之旅》票房破4亿
山东高速股东户数下降2.82%,户均持股74.45万元
用户吐槽下载视频会员到期不能看 客服:需重新开通会员
天天快资讯:官方认证“全国美食地图”来了:快看看你家乡上榜没
【播资讯】业界首创!台铃发布水冷氢能两轮车:备用氢罐随身带 续航无压力
中科院联合研发技术!小米米家全效空气净化器明天开售:首发3699元
环球速递!苹果WWDC大会定档!iOS 17将至 看看你的iPhone还能不能升
【世界新要闻】NEXON新作TPS《幕后高手》开启不限量最终测试
使用pinctrl和gpio子系统开发GPIO驱动正点原子IMX6ULL阿尔法板的LED灯
Topaz Video AI V3.1.10 视频降噪升画质工具 安装教程(windows/macos版)
热讯:DBSAT脚本快速收集方法
实战SQL优化(以MySQL深分页为例)
世界看热讯:如何限制进程内存:cgroup
时讯:每日机构分析:3月30日
世界播报:空调安装莫任性,城管上门解民忧
博主揭秘成都夜市“鬼秤”套路:10家有8家不准!官方已开展调查
每日讯息!车企回应试驾翻车后 新款坦克500正常上市:33.50万起买高安全硬派越野
索尼PS VR2销量滑铁卢:首周仅有不足30万
天天热文:拒绝山寨卡、翻新卡!NVIDIA联合七彩虹等厂商重拳出击
每日聚焦:CloudQuery 社区版重启 | 我们做了哪些准备?
世界播报:GraphPad Prism 9 V9.5.1 破解版安装教程
世界讯息:python中类成员修饰符
因为一篇文章,这国将召见俄大使
全球最新:改革举措加快推出 “首次”“首单”不断涌现——金融部门努力提升金融服务实体经济能力
【时快讯】抖音发布公告:“剧情演绎”类视频需注明 否则最严无限期封禁
全球热点评!怀念原版《RE4》艾什莉 玩家们绘制老鼠艾什莉同人图
世界简讯:单休女生30小时往返1300公里游6景点:第二天还要上班
速讯:上海交大教授李峰:医生没有创造力 非常容易被AI替代
澳大利亚多地暴雨洪灾 泳池频现剧毒漏斗蜘蛛:最快15分钟致死
当前头条:目标规模超30亿,浙江衢州拟设一支产业基金
天天最资讯丨04735-数据库系统原理知识点
【快播报】【验证码逆向专栏】极验三代、四代点选类验证码逆向分析
Linux 服务器大量的 log 日志,如何正确看日志快速定位错误?你必须学会!
天天最资讯丨APS系统设计经验分享(时间推导 - 2023.03)
每日动态!一个诡异的Python小问题
全球球精选!科创板收盘播报:科创50指数涨0.33% 半导体股延续强势表现