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


    全局 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;
        }
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

    代码涉及到的类有

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

    TraceIdUtil源码如下

    public class TraceIdUtil {
    
        private static final ThreadLocal TRACE_ID = new ThreadLocal();
    
        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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    代码如下

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

    其中 InvokerInvocationHandler 调用数据的处理及rpc调用实体的封装过程。所以我们需要在调用的起始位置添加traceId信息 。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tOF7LMvb-1660607242005)(http://wx4.sinaimg.cn/mw1024/006QW2Smgy1fv4kysnuvnj30kf0fw75e.jpg)]

    其次是服务提供者进行请求处理过程: 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    资源文件夹下创建 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 implements Invoker {
        public Result invoke(Invocation inv) throws RpcException {
            ......
            Map 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);
            ......
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    文章来源:https://my.oschina.net/LucasZhu/blog/2046356
    推荐阅读:https://www.roncoo.com/course/list.html?courseName=Dubbo

    )

  • 相关阅读:
    springmvc第十四个练习(异常处理)
    昇思25天学习打卡营第五天|应用实践/计算机视觉/FCN图像语义分割
    Scala第十五章节
    markdown语法大全_Markdown超详细介绍
    关于Nginx缓存
    数据库系统的重做日志大体上长得什么样?(数据库理论)
    web前端期末大作业——仿小米商城电商平台(6页) html+css+javascript网页设计实例 企业网站制作
    Prometheus 基本概念
    MATLAB数据导入
    C++11 - 3 - lambda表达式
  • 原文地址:https://blog.csdn.net/web15085599741/article/details/126358000