正文
Dubbo链路追踪——生成全局ID(traceId)
原
荐
字数 899
阅读 4
收藏 0
Dubbo
开源中国十周年庆:开源众包怎么做我说了算!参与赢终身免费大奖 >>>
关于链路追踪,在微服务的趋势下,一次调用的日志信息分布在不同的机器上或目录下,当需要看一条链路调用所有的日志信息时,这是个比较困难的地方,我们虽然有ELK , Sentry等日志异常收集分析工具, 但是如何把信息串起来也是一个关键的问题。 我们一般的做法是在系统调用开始时生成一个traceId , 并且它伴随着一次调用的整个生命周期 。 当一个服务调用另外一个服务的时候,traceId 则向下透传,全局使用唯一一个。
我们通过分析源码可以知道客户端在调用服务段进行服务消费时,实际上发送的是封装过的Request实体 ,Data为Invocation实体对象(接口签名,参数类型,参数值,及attachment附件)
final class HeaderExchangeChannel implements ExchangeChannel { ... public ResponseFuture request(Object request, int timeout) throws RemotingException { if (closed) { throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); } // create request. Request req = new Request(); req.setVersion("2.0.0"); req.setTwoWay(true); req.setData(request); DefaultFuture future = new DefaultFuture(channel, req, timeout); try { channel.send(req); } catch (RemotingException e) { future.cancel(); throw e; } return future; } ... }
通过源码可知 request是入参,其他参数均为固定,所以只能在request中做文章。
TraceIdUtil源码如下
public class TraceIdUtil { private static final ThreadLocal<String> TRACE_ID = new ThreadLocal<String>(); public static String getTraceId() { if(TRACE_ID.get() == null) { String s = UUID.randomUUID().toString(); setTraceId(s); } return TRACE_ID.get(); } public static void setTraceId(String traceId) { TRACE_ID.set(traceId); } }
由 Dubbo线程模型图示 可知,Dubbo客户端调用实际上通过JavassistProxyFactory获取的是Proxy代理对象。 代码如下
public class JavassistProxyFactory extends AbstractProxyFactory { ... @SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); } ... }
其中 InvokerInvocationHandler 调用数据的处理及rpc调用实体的封装过程。所以我们需要在调用的起始位置添加traceId信息 。
其次是服务提供者进行请求处理过程: DubboProtocol的requestHandler 请求处理器(不明白请详读服务Dubbo服务暴露过程源码)
将traceId 从RpcInvocation 的attachment属性中取出 ,传给TraceIdUtil 方面后续调用过程使用。
创建Filter 服务提供者端扩展
/** * Created with IntelliJ IDEA. * * @author: bakerZhu * @description: * @time: 2018年09月09日 * @modifytime: */ @Activate(group = {Constants.CONSUMER, Constants.PROVIDER} , order = -9999) public class GlobalTraceFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { String traceId = invocation.getAttachment("traceId"); if(!StringUtils.isBlank(traceId)) { RpcContext.getContext().setAttachment("traceId",traceId); }else { // 第一次发起调用 RpcContext.getContext().setAttachment("traceId", UUID.randomUUID().toString()); } return invoker.invoke(invocation); } }
资源文件夹下创建 META-INF/dubbo 文件夹 创建com.alibaba.dubbo.rpc.Filter 文件,并编辑文件内容 gtrace=com.alibaba.dubbo.rpc.filter.GlobalTraceFilter
详见 AbstractInvoker.invoke(Invocation inv) 方法
public abstract class AbstractInvoker<T> implements Invoker<T> { public Result invoke(Invocation inv) throws RpcException { ...... Map<String, String> context = RpcContext.getContext().getAttachments(); if (context != null) { invocation.addAttachmentsIfAbsent(context); } if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) { invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString()); } RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); ...... } }
将上下文的“附件信息”拷贝到RpcInvocation中
© 著作权归作者所有
共有人打赏支持
粉丝 79
博文 556
码字总数 563559
作品 0
通州
程序员
相关文章 最新文章
分布式系统调用链监控 应用架构由集中式向分布式演进后,整个调用关系变得复杂。 分布式架构由复杂且较大规模集群构成,各个应用之间相当独立,可能由不同团队、不同语言实现。 系统一个完整...
wangyangzhizhou
2016/12/23
0
0
郑昀 最后更新于2014/11/12 GoogleDapper、分布式跟踪、鹰眼、Tracing、HBase、HDFS、 本文档适用人员:研发 分布式系统为什么需要 Tracing? 先介绍一个概念:分布式跟踪,或分布式追踪。 ...
旁观者-郑昀
2014/12/17
0
1
分布式调用链跟踪系统通常有几个设计目标 低侵入性 -- 作为非业务组件,应当尽可能少侵入或者无侵入其他业务系统,对于使用方透明,减少开发人员的负担; 灵活的应用策略 -- 可以(最好随时)...
ginobefun
2017/05/25
0
0
https://github.com/Yirendai/cicada/blob/master/cicada-docs/cicadadesign.md 背景与目标 面对日趋复杂的分布式系统,如服务框架、消息中间件、缓存、数据层等,我司在业务性能瓶颈定位、故...
tantexian
2016/11/02
547
1
ZipKin入门介绍 Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开发贡献。其主要功能是聚集来自各个异构系...
qq924862077
05/12
0
0
没有更多内容
加载失败,请刷新页面
加载更多Thread的run()与start()的区别 java的线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都是通过某个特...
DemonsI
12分钟前
0
0
由于 golang.org 在国内已被墙,不能直接使用go get安装对应的包,但是可以通过github间接安装。 因为 golang.org/x/xxx 这类包托管在 github.com/golang,从这里找到相应的包即可。比如 go...
FalconChen
14分钟前
0
0
在网上刷小视频的时候可以发现,有很多视频是由多个小视频合并到一起的,我们也可以将多个视频片段合并到一起,制作成一个完整视频。对多个视频片段进行合并的好处就是,让你在观看视频的...
萤火的萤火
19分钟前
1
0
1.首先找到你的系统安装文件 如:cn_windows_8.1_professional_vl_with_update_x64_dvd_4050293.iso 2.解压出来的路径: E:/cn_windows_8.1_professional_vl_with_update_x64_dvd_4050293 3.找......
Kxvz
21分钟前
0
0
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在...
飓风2000
22分钟前
0
0
没有更多内容
加载失败,请刷新页面
加载更多