Dubbo链路追踪——生成全局ID(traceId)

全局 traceId

关于链路追踪,在微服务的趋势下,一次调用的日志信息分布在不同的机器上或目录下,当需要看一条链路调用所有的日志信息时,这是个比较困难的地方,我们虽然有ELK , Sentry等日志异常收集分析工具, 但是如何把信息串起来也是一个关键的问题。 我们一般的做法是在系统调用开始时生成一个traceId , 并且它伴随着一次调用的整个生命周期 。 当一个服务调用另外一个服务的时候,traceId 则向下透传,全局使用唯一一个。

一、通过修改Dubbo源码实现全局traceId(侵入性高不建议使用)

我们通过分析源码可以知道客户端在调用服务段进行服务消费时,实际上发送的是封装过的Request实体 ,Data为Invocation实体对象(接口签名,参数类型,参数值,及attachment附件)

1final class HeaderExchangeChannel implements ExchangeChannel { 2... 3 public ResponseFuture request(Object request, int timeout) throws RemotingException { 4 if (closed) { 5 throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); 6 } 7 // create request. 8 Request req = new Request(); 9 req.setVersion("2.0.0"); 10 req.setTwoWay(true); 11 req.setData(request); 12 DefaultFuture future = new DefaultFuture(channel, req, timeout); 13 try { 14 channel.send(req); 15 } catch (RemotingException e) { 16 future.cancel(); 17 throw e; 18 } 19 return future; 20 } 21... 22}

通过源码可知 request是入参,其他参数均为固定,所以只能在request中做文章。

代码涉及到的类有

  • TraceIdUtil : 链路追踪全局ID生成器
  • InvokerInvocationHandler : 消费端Invocation处理器
  • DubboProtocol : Dubbo协议处理入口,内有服务提供者处理请求数据信息的Handler处理器

TraceIdUtil源码如下

1public class TraceIdUtil { 2 3 private static final ThreadLocal<String> TRACE_ID = new ThreadLocal<String>(); 4 5 public static String getTraceId() { 6 if(TRACE_ID.get() == null) { 7 String s = UUID.randomUUID().toString(); 8 setTraceId(s); 9 } 10 return TRACE_ID.get(); 11 } 12 13 public static void setTraceId(String traceId) { 14 TRACE_ID.set(traceId); 15 } 16}

Dubbo线程模型图示可知,Dubbo客户端调用实际上通过JavassistProxyFactory获取的是Proxy代理对象。 Dubbo线程模型图示 代码如下

1public class JavassistProxyFactory extends AbstractProxyFactory { 2 ... 3 @SuppressWarnings("unchecked") 4 public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { 5 return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); 6 } 7 ... 8}

其中 InvokerInvocationHandler 调用数据的处理及rpc调用实体的封装过程。所以我们需要在调用的起始位置添加traceId信息 。 traceId

其次是服务提供者进行请求处理过程: DubboProtocol的requestHandler 请求处理器(不明白请详读服务Dubbo服务暴露过程源码) traceId

将traceId 从RpcInvocation 的attachment属性中取出 ,传给TraceIdUtil 方面后续调用过程使用。

扩展Provider端Filter (无侵入性,建议使用)

创建Filter 服务提供者端扩展

1/** 2 * Created with IntelliJ IDEA. 3 * 4 * @author: bakerZhu 5 * @description: 6 * @time: 2018年09月09日 7 * @modifytime: 8 */ 9@Activate(group = {Constants.CONSUMER, Constants.PROVIDER} , order = -9999) 10public class GlobalTraceFilter implements Filter { 11 12 @Override 13 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { 14 String traceId = invocation.getAttachment("traceId"); 15 if(!StringUtils.isBlank(traceId)) { 16 RpcContext.getContext().setAttachment("traceId",traceId); 17 }else { // 第一次发起调用 18 RpcContext.getContext().setAttachment("traceId", UUID.randomUUID().toString()); 19 } 20 return invoker.invoke(invocation); 21 } 22}

资源文件夹下创建 META-INF/dubbo 文件夹 创建com.alibaba.dubbo.rpc.Filter 文件,并编辑文件内容 gtrace=com.alibaba.dubbo.rpc.filter.GlobalTraceFilter

疑惑点 为什么设置Dubbo上下文的attachment调用时会出现在 RpcInvocation中

详见 AbstractInvoker.invoke(Invocation inv) 方法

1public abstract class AbstractInvoker<T> implements Invoker<T> { 2 public Result invoke(Invocation inv) throws RpcException { 3 ...... 4 Map<String, String> context = RpcContext.getContext().getAttachments(); 5 if (context != null) { 6 invocation.addAttachmentsIfAbsent(context); 7 } 8 if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) { 9 invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString()); 10 } 11 RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); 12 ...... 13 } 14}

将上下文的“附件信息”拷贝到RpcInvocation中

赞赏支持

赞赏支持

点赞
收藏

评论区

加载中...

相关推荐

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Dubbo日志链路追踪TraceId选型

!mark(https://oscimg.oschina.net/oscnet/updd1ad9729fb807ee6dc473bdc283b1a4481.png)一、目的开发排查系统问题用得最多的手段就是查看系统日志,但是在分布式环境下使用日志定位问题还是比较麻烦,需要借助全链路追踪ID把上下文串联起来,本文主要分享基于

Dubbo + Zipkin + Brave实现全链路追踪

DubboZipkinBrave实现全链路追踪最近写了一个链路追踪Demo分享下,实现了链路追踪过程中数据的记录,还有能扩展的地方,后期再继续补充。原理参考上面文章《Dubbo链路追踪——生成全局ID(traceId)》(https://my.oschina.net/Luc

Spring Boot + Spring Cloud 构建微服务系统(八):分布式链路追踪(Sleuth、Zipkin)

技术背景在微服务架构中,随着业务发展,系统拆分导致系统调用链路愈发复杂,一个看似简单的前端请求可能最终需要调用很多次后端服务才能完成,那么当整个请求出现问题时,我们很难得知到底是哪个服务出了问题导致的,这时就需要解决一个问题,如何快速定位服务故障点,于是,分布式系统调用链追踪技术就此诞生了。ZipKinZipkin是一个

Twitter的分布式自增ID算法snowflake (Java版)

概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移