Dubbo链路追踪——生成全局ID(traceId)
2018-09-10 19:07
991 查看
全局 traceId
关于链路追踪,在微服务的趋势下,一次调用的日志信息分布在不同的机器上或目录下,当需要看一条链路调用所有的日志信息时,这是个比较困难的地方,我们虽然有ELK , Sentry等日志异常收集分析工具, 但是如何把信息串起来也是一个关键的问题。我们一般的做法是在系统调用开始时生成一个traceId , 并且它伴随着一次调用的整个生命周期 。 当一个服务调用另外一个服务的时候,traceId 则向下透传,全局使用唯一一个。
一、通过修改Dubbo源码实现全局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 : 链路追踪全局ID生成器InvokerInvocationHandler : 消费端Invocation处理器
DubboProtocol : Dubbo协议处理入口,内有服务提供者处理请求数据信息的Handler处理器
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 方面后续调用过程使用。
扩展Provider端Filter (无侵入性,建议使用)
创建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
疑惑点 为什么设置Dubbo上下文的attachment调用时会出现在 RpcInvocation中
详见 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中
相关文章推荐
- 如何在高并发分布式系统中生成全局唯一Id
- 【转】高并发分布式系统中生成全局唯一Id汇总
- 分布式ID生成方法-趋势有序的全局唯一ID
- 高并发分布式系统中生成全局唯一Id汇总
- 【转】如何在高并发分布式系统中生成全局唯一Id
- 【架构】生成全局唯一ID的3个思路,来自一个资深架构师的总结
- 如何在高并发分布式系统中生成全局唯一Id
- 如何在高并发分布式系统中生成全局唯一Id
- MySQL分库分表环境下全局ID生成方案
- 如何在高并发分布式系统中生成全局唯一Id
- 高并发 sql 生成不重复编号 (订单号) & 如何在高并发分布式系统中生成全局唯一Id
- 分库分表的情况下生成全局唯一的ID
- 【php】mysql全局ID生成方案
- (备忘)c++中生成全局唯一id
- 高并发分布式环境中获取全局唯一ID[分布式数据库全局唯一主键生成]
- Dubbo + Zipkin + Brave实现全链路追踪
- 如何在高并发分布式系统中生成全局唯一Id
- 基于业务对Twitter生成全局唯一ID的SnowFlake算法的改造
- 分布式存储中,生成全局唯一ID的几种方案