PDF书籍《手写调用链监控APM系统-Java版》第11章 插件与链路的结合:HttpClient插件实现跨进程传输TraceSegment

devtools/2024/12/29 2:52:31/

本人阅读了 Skywalking 的大部分核心代码,也了解了相关的文献,对此深有感悟,特此借助巨人的思想自己手动用JAVA语言实现了一个 “调用链监控APM” 系统。本书采用边讲解实现原理边编写代码的方式,看本书时一定要跟着敲代码。

作者已经将过程写成一部书籍,奈何没有钱发表,如果您知道渠道可以联系本人。一定重谢。

本书涉及到的核心技术与思想

JavaAgent , ByteBuddy,SPI服务,类加载器的命名空间,增强JDK类,kafka,插件思想,切面,链路栈等等。实际上远不止这么多,差不多贯通了整个java体系。

适用人群

自己公司要实现自己的调用链的;写架构的;深入java编程的;阅读Skywalking源码的;

版权

本书是作者呕心沥血亲自编写的代码,不经同意切勿拿出去商用,否则会追究其责任。

原版PDF+源码请见:

本章涉及到的工具类也在这里面:

PDF书籍《手写调用链监控APM系统-Java版》第1章 开篇介绍-CSDN博客

第11章 插件与链路的结合:HttpClient插件实现跨进程传输TraceSegment

之所以要讲这个插件就是因为http调用涉及到跨进程传输,会有新的TraceSegment生成,需要把当前TraceSegment信息设置到Http请求头里面进行跨进程传输到后一个TraceSegment上面。

要想对httpclient的接口请求进行拦截,需要拦截的信息如下:

类名:org.apache.commons.httpclient.HttpClient

方法:executeMethod

非JDK类库

下面来开发这个插件,要记住前面说的插件开发四部曲。在插件模块下新增http-client-plugin项目,hadluo-apm-plugin.def内容如下:

http-client=com.hadluo.apm.httpclient.HttpClientInstrumentation

HttpClientInstrumentation代码如下:

public class HttpClientInstrumentation extends AbstractClassEnhancePluginDefine {@Overridepublic String enhanceClass() {// 拦截类return "org.apache.commons.httpclient.HttpClient";}@Overridepublic MethodsInterceptPoint[] configMethodsInterceptPoint() {return new MethodsInterceptPoint[]{new MethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {// 拦截方法return ElementMatchers.named("executeMethod").and(ElementMatchers.takesArguments(HttpMethod.class));}@Overridepublic String getMethodsInterceptor() {// 拦截处理逻辑return "com.hadluo.apm.httpclient.HttpClientInterceptor";}@Overridepublic boolean isOverrideArgs() {return false;}}};}
}

HttpClientInterceptor代码如下:

public class HttpClientInterceptor implements InstanceMethodsAroundInterceptor {@Overridepublic void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes) throws Throwable {HttpMethod httpMethod = (HttpMethod) allArguments[0];final String remotePeer = httpMethod.getURI().getHost() + ":" + httpMethod.getURI().getPort();ContextCarrier contextCarrier = new ContextCarrier();TraceContextManager service = ServiceManager.INSTANCE.getService(TraceContextManager.class);// 创建span , 并且把当前segment的信息 设置到 contextCarrier里面AbstractSpan exitSpan = service.createExitSpan(httpMethod.getURI().getPath(), remotePeer, contextCarrier);// 设置标签等信息exitSpan.setComponent("HttpClient");exitSpan.setLayer(SpanLayer.HTTP) ;exitSpan.setTag("url" , httpMethod.getURI().toString()) ;exitSpan.setTag("method" , httpMethod.getName()) ;// 将contextCarrier 信息设置到 http请求头里面, 传递到下一个 跨进程的segment上Map<String, String> serialize = contextCarrier.serialize();for(String key : serialize.keySet()){httpMethod.setRequestHeader(key, serialize.get(key));}}@Overridepublic Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {TraceContextManager service = ServiceManager.INSTANCE.getService(TraceContextManager.class);service.stopSpan();return ret;}@Overridepublic void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {TraceContextManager service = ServiceManager.INSTANCE.getService(TraceContextManager.class);service.activeSpan().log(t) ;}
}

我们首先构造了一个空的ContextCarrier , 然后传递到createExitSpan方法里面,这个方法我们之前只实现了两个参数的,我们需要增加这个重载方法的实现。在TraceContextManager增加方法:

public AbstractSpan createExitSpan(String operationName, String remotePeer , ContextCarrier contextCarrier){AbstraceTraceContext context = getOrCreate(true) ;AbstractSpan exitSpan = context.createExitSpan(operationName, remotePeer);context.inject(contextCarrier);return exitSpan ;
}

通过inject方法,我们将context上的segment信息设置到了空的ContextCarrier 上面, inject代码如下:

 @Override
public void inject(ContextCarrier carrier) {carrier.setTraceId(traceSegment.getTraceId());carrier.setTraceSegmentId(traceSegment.getTraceSegmentId());carrier.setSpanId(acviveSpan().getSpanId());carrier.setParentServiceInstance(Config.Agent.serviceInstance);carrier.setParentServiceName(Config.Agent.serviceName);
}

经过这个设置,我们的ContextCarrier 里面就有当前的segment信息了,然后回到插件的beforeMethod, 下面将ContextCarrier 的信息序列化到map里面,serialize也是新增的,代码如下:

public Map<String, String> serialize(){Map<String, String> param = new HashMap<String, String>();for (Field f : this.getClass().getDeclaredFields()) {if(Modifier.isStatic(f.getModifiers())){continue;}f.setAccessible(true);try {param.put(SW_FLAG + f.getName() , f.get(this).toString()) ;} catch (IllegalAccessException e) {Logs.err(getClass(), "ContextCarrier serialize错误, field: " + f.getName(), e);}}return param ;
}

通过这个serialize序列化后,就把信息转存到了map里面,最后通过遍历Map,httpMethod.setRequestHeader将每个值设置到了请求头里面传递到下一个进程。

由于代码过于简单,作者就不在测试此插件,读者也应该能自己测试了,有问题的可以向作者提出,万分感谢!


http://www.ppmy.cn/devtools/146280.html

相关文章

Datawhale AI 冬令营学习笔记-零编程基础制作井字棋小游戏

井字棋小游戏是通过豆包MarsCode实现的&#xff0c;没有改动任何的代码&#xff0c;全部是通过对话让AI进行优化和改进。 开始进入正题&#xff1a;进入豆包MarsCode在线IDE&#xff0c;直接点击上方蓝字&#xff0c;或复制链接打开: 豆包 MarsCode - 编程助手。 IDE界面&…

vue中的css深度选择器v-deep 配合!important

当 <style> 标签有 scoped 属性时&#xff0c;它的 CSS 只作用于当前组件中的元素&#xff0c;父组件的样式将不会渗透到子组件。 如果你希望 scoped 样式中的一个选择器能够作用得“更深”&#xff0c;例如影响子组件&#xff0c;你可以使用深度选择器。 ::v-deep { } 举…

TCP Vegas拥塞控制算法——baseRtt 和 minRtt的区别

1. baseRtt&#xff08;连接过程中的最小 RTT&#xff09; baseRtt 是在整个连接过程中观察到的最小 RTT&#xff08;往返时间&#xff09;。通常它是在连接初始化时或者在网络的稳定状态下&#xff0c;测量并记录的最小 RTT 值。 baseRtt 用来作为衡量网络传输速率的基准。它…

transformer用作分类任务

系列博客目录 文章目录 系列博客目录1、在手写数字图像这个数据集上进行分类1. 数据准备2. 将图像转化为适合Transformer的输入3. 位置编码4. Transformer编码器5. 池化操作6. 分类头7. 训练8. 评估总结流程&#xff1a;相关模型&#xff1a; 1、在手写数字图像这个数据集上进行…

Springboot jar包加密加固并进行机器绑定

获取机器码&#xff0c;通过classfinal-fatjar-1.2.1.jar来获取机器码 命令&#xff1a;java -jar classfinal-fatjar-1.2.1.jar -C 对springboot打包的jar进行加密功能 java -jar classfinal-fatjar-1.2.1.jar -file lakers-ljxny-3.0.0.jar -packages com.lygmanager.laker…

华为 AI Agent:企业内部管理的智能变革引擎(11/30)

一、华为 AI Agent 引领企业管理新潮流 在当今数字化飞速发展的时代&#xff0c;企业内部管理的高效性与智能化成为了决定企业竞争力的关键因素。华为&#xff0c;作为全球领先的科技巨头&#xff0c;其 AI Agent 技术在企业内部管理中的应用正掀起一场全新的变革浪潮。 AI Ag…

深入理解.NET内存回收机制

&#xff3b;前言&#xff1a;&#xff3d;.Net平台提供了许多新功能&#xff0c;这些功能能够帮助程序员生产出更高效和稳定的代码。其中之一就是垃圾回收器(GC)。这篇文章将深入探讨这一功能&#xff0c;了解它是如何工作的以及如何编写代码来更好地使用这一.Net平台提供的功…

nginx-1.23.2版本RPM包发布

nginx-1.23.2-0.x86_64.rpm用于CentOS7系统的安装&#xff0c;安装路径与编译安装是同一个路径。安装方法&#xff1a; 将nginx-1.23.2-0.x86_64.rpm上传至目标服务器&#xff0c;执行rpm -ivh nginx-1.23.2-0.x86_64.rpm命令进行安装。 卸载方法&#xff1a; 卸载前先将nginx服…