分布式链路追踪 —— 基于Dubbo的traceId追踪传递

文章目录

  • 原文链接
  • RpcContext 上下文对象
  • Dubbo 过滤器(Filter)对象
  • 基于Dubbo的traceId追踪传递实现

原文链接

RpcContext 上下文对象

在实现 Dubbo 调用之间的链路跟踪之前,先简单了解 RpcContext 上下文对象和 Filter 过滤器对象,Dubbo 分布式链路追踪是基于这两个对象实现。

概要

  • RpcContext 是 Dubbo 框架提供的一个类,它的设计目标是提供一个 dubbo 调用的上下文对象,用于在远程过程调用(RPC)期间传递、共享请求和响应的相关信息。它可以用于存储、访问与当前 RPC 调用相关的数据,如调用方的 IP 地址、附加参数、上下文变量等。它提供了一些静态方法和属性,可以方便地获取和设置与当前线程相关的 RPC 上下文。

    public class RpcContext {
    
        private static final InternalThreadLocal<RpcContext> LOCAL = new InternalThreadLocal<RpcContext>() {
            protected RpcContext initialValue() {
                return new RpcContext();
            }
        };
        private static final InternalThreadLocal<RpcContext> SERVER_LOCAL = new InternalThreadLocal<RpcContext>() {
            protected RpcContext initialValue() {
                return new RpcContext();
            }
        };
        private final Map<String, String> attachments = new HashMap();
        private final Map<String, Object> values = new HashMap();
        // ......
    }
    

原理

  • RpcContext 是基于 ThreadLocal 实现的,做到了线程隔离。RpcContext是与线程绑定的,每个线程都有自己的一个 RpcContext 实例并使用 ThreadLocal 变量来存储,避免并发访问问题。当客户端发起一个 RPC 请求时,Dubbo 框架会创建一个新的线程来处理该请求,并且会将 RpcContext 与该线程进行绑定,这样看,对于每次 RPC 请求,RpcContext 也是唯一的。但对于同一个线程内的多个 RPC 请求,它们共享同一个 RpcContext 实例。

  • RpcContext 实例会在请求处理期间一直存在,并在请求处理完成后需要清理当前线程上的 RpcContext 实例中的数据,以确保下次使用该线程处理新的请求时,RpcContext 是一个干净的状态。

    对于服务消费方,Dubbo 框架在请求发送、响应后没有清除 RpcContext 实例中的数据;

    对于服务提供方,Dubbo 框架在收到请求并处理后,会去清除 RpcContext 实例中的数据,这个在 ContextFilter 服务提供方过滤器中可以看到。

使用场景

  • 可以在 dubbo 的拦截器、过滤器或服务提供者/消费者的代码中使用 RpcContext 来获取和设置上下文信息,以满足特定的业务需求,如日志跟踪、传递身份验证信息等。

下面是一些常用的 RpcContext 方法和属性:

  • RpcContext.getContext(): 获取当前线程的 RpcContext 实例。
  • RpcContext.isConsumerSide(): 判断当前线程是否处于消费者端。
  • RpcContext.isProviderSide(): 判断当前线程是否处于提供者端。
  • RpcContext.getRemoteAddress(): 获取远程调用的地址。
  • RpcContext.getLocalAddress(): 获取本地调用的地址。
  • RpcContext.setAttachment(String key, String value): 设置附加参数。
  • RpcContext.getAttachment(String key): 获取指定键的附件信息。
  • RpcContext.getAttachments(): 获取所有的附件信息。

Dubbo 过滤器(Filter)对象

Dubbo Filter 介绍

dubbo 的 Filter 是 dubbo 框架提供的一个功能扩展点,用于对服务提供者和消费者之间的请求和响应进行拦截过滤处理,比如认证和授权、日志跟踪、传递一些公共信息等。

如 dubbo 原生 Filter 实现类,如:ConsumerContextFilter 和 ContextFilter

  • ConsumerContextFilter 是一个服务消费方的过滤器,用于在服务消费者发起 RPC 调用之前或之后,对上下文信息进行处理和传递,用于收集和发送调用方的上下文信息到服务提供者端。

    ConsumerContextFilter 源码:通过这个过滤器可以看到在 RPC 调用之前会获取 RpcContext 对象并设置相关参数,Dubbo 框架会借助 RpcContext 对象将相关数据透传到服务提供方。

    @Activate(
        group = {"consumer"},
        order = -10000
    )
    public class ConsumerContextFilter implements Filter {
        public ConsumerContextFilter() {
        }
    
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            RpcContext.getContext().setInvoker(invoker).setInvocation(invocation).setLocalAddress(NetUtils.getLocalHost(), 0).setRemoteAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort()).setRemoteApplicationName(invoker.getUrl().getParameter("remote.application")).setAttachment("remote.application", invoker.getUrl().getParameter("application"));
            if (invocation instanceof RpcInvocation) {
                ((RpcInvocation)invocation).setInvoker(invoker);
            }
    
            return invoker.invoke(invocation);
        }
    }
    

    @Activate 注解是 Dubbo 框架提供的一个扩展点激活注解,用于指定在特定条件下激活扩展点。

    在上述过滤器中,@Activate(group = Constants.CONSUMER, order = -10000) 是对一个扩展点的激活配置。具体解释如下:

    • group = {"consumer"}: 在指定的分组激活,如 consumer,表示该扩展点在消费者端被激活。
    • order = -10000: 指定激活的顺序为 -10000。在 Dubbo 框架中,扩展点的激活顺序可以通过 order 值来进行控制,值越小表示优先级越高。
  • ContextFilter 是一个服务提供方的过滤器,用于在服务提供者收到 RPC 调用请求之前或之后,对上下文信息进行处理和传递,如清除 RpcContext 实例中的数据,以确保下次使用该线程处理新的请求时,RpcContext 是一个干净的状态。

    ContextFilter 源码:在处理请求后去清除 RpcContext 中相关数据。

    @Activate(
        group = {"provider"},
        order = -10000
    )
    public class ContextFilter implements Filter, Filter.Listener {
        private static final String TAG_KEY = "dubbo.tag";
        public ContextFilter() {
        }
        
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            // ......
            Result var6;
            try {
                // 禁止清除RpcContext的功能。这意味着在调用invoker.invoke(invocation)之后,RpcContext中的上下文信息不能被清除。
                RpcContext.getContext().clearAfterEachInvoke(false);
                var6 = invoker.invoke(invocation);
            } finally {
                // 开启允许清除RpcContext的功能。
                RpcContext.getContext().clearAfterEachInvoke(true);
                // 显式清除RpcContext中的上下文信息。
                RpcContext.removeContext();
                RpcContext.removeServerContext();
            }
    
            return var6;
        }
        // ......
    }
    

自定义 Dubbo Filter

  1. 实现 org.apache.dubbo.rpc.Filter接口:

    import org.apache.dubbo.rpc.*;
    
    public class CustomFilter implements Filter {
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            // 自定义过滤逻辑
            return invoker.invoke(invocation);
        }
    }
    
  2. 创建 Dubbo 的 SPI 扩展文件(META-INF/dubbo/org.apache.dubbo.rpc.Filter)中,将自定义过滤器的实现类指定为对应的扩展点:

    customFilter=com.example.CustomFilter
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  3. 使过滤器生效:

    方式一:在自定义过滤器上使用注解:

    @Activate(group = {"consumer"})
    

    方式二:在配置文件(这里使用application.properties)中配置:

    dubbo.consumer.filter=customFilter
    

    通过以上步骤,在服务消费方指定了一个自定义过滤器,该过滤器将在服务消费者发起远程调用前后执行自定义的逻辑。

基于Dubbo的traceId追踪传递实现

要实现在 Dubbo 接口之间传递 TraceID,可以使用 Dubbo 的拦截器(Filter)机制来实现。下面是一个示例代码,演示了如何在 Dubbo 接口调用中传递 TraceID 进行追踪,其中具体 Filter 实现过程前面已讲述,这里只展示实现类代码。需要 demo 示例代码,请关注【Qin的学习营地】,回复【基于Dubbo的traceId追踪传递】。

这里使用 spring boot 整合 dubbo,详细搭建过程请参考:Dubbo 快速入门使用教程

这里通过打印日志来可视化结果,使用了 Slf4J 的 MDC,通过设置 MDC.put(key, value),并在日志配置文件中配置 key,日志打印时会将配置 key 的地方转换为 value 打印出来。

  1. 创建 Dubbo 的服务提供方拦截器类,从 RpcContext 中获取 traceid 参数,并设置到 MDC 中,请求处理完后清除 MDC 中的 traceid 参数:

    @Slf4j
    @Activate(group = {"provider"})
    public class TraceIdProviderFilter implements Filter {
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            String traceId = RpcContext.getContext().getAttachment("traceId");
            if (traceId != null) {
                MDC.put("traceId", traceId);
            }
            try {
                return invoker.invoke(invocation);
            } finally {
                MDC.remove("traceId");
            }
        }
    }
    
  2. 创建 Dubbo 的服务消费方拦截器类,向 RpcContext 中写入 traceid 参数:

    @Slf4j
    @Activate(group = {"consumer"})
    public class TraceIdConsumerFilter implements Filter {
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            String traceId = MDC.get("traceId");
            if (traceId == null) {
                traceId = UUID.randomUUID().toString().replace("-", "");
            }
            RpcContext.getContext().setAttachment("traceId", traceId);
            MDC.put("traceId", traceId);
            log.info("consumer ——> provider");
            return invoker.invoke(invocation);
        }
    }
    
  3. 服务消费方调用逻辑:

    @Slf4j
    @Component
    public class ProducerService {
    
        @Reference(retries = -1, version="1.0.0", timeout = 15000)
        private HelloService helloService;
    
        public String consumerSayHello(String name){
            String traceId = UUID.randomUUID().toString().replace("-", "");
            MDC.put("traceId", traceId);
            String hello = helloService.sayHello(name);
            log.info("consumer receive response : "+ hello);
            return hello;
        }
    }
    
  4. 运行后看日志打印结果,可以看到服务提供方的 traceId 和服务消费方的 traceId 两者一致,服务消费方的 traceId 透传到服务提供方。

    消费方:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    提供方:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 分布式链路追踪
基于Dubbo的traceId追踪传递

本文首先介绍 Dubbo 的 RpcContext 上下文和 Filter 过滤器,然后再介绍基于Dubbo的traceId追踪传递的实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/255832.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue使用xlsx和xlsx-style导出xlsx文件并修改样式

1.下载依赖 npm install xlsx --save npm install file-saver --save npm install xlsx-style --save2.先修改xlsx-style的源码&#xff0c;一旦引入xlsx-style则会报错 在\node_modules\xlsx-style\dist\cpexcel.js 807行 的 var cpt require(’./cpt’ ‘able’); 改成 v…

接口测试的工具(3)----postman+node.js+newman

1.安装newman&#xff1a;输入命令之后 一定注意 什么都不要操作 静静的等待结束就行了。 2.安装失败的对此尝试不行 在用下面的方法 解压一下就行了 3.验证是否成功 多次尝试是可以在线安装成功的

开发企业展示小程序的关键步骤和技巧

随着移动互联网的快速发展&#xff0c;小程序已经成为企业展示形象、推广产品和服务的重要工具。拥有一个优秀的小程序可以帮助企业提高品牌知名度&#xff0c;吸引更多潜在客户&#xff0c;提升用户体验。以下是拥有一个展示小程序的步骤&#xff1a; 确定需求和目标 首先&am…

21、同济、微软亚研院、西安电子科技大提出HPT:层次化提示调优,独属于提示学习的[安妮海瑟薇]

前言&#xff1a; 本论文由同济大学、微软亚洲研究院、西安电子科技大学&#xff0c;于2023年12月11日中了AAAI2024 论文&#xff1a; 《Learning Hierarchical Prompt with Structured Linguistic Knowledge for Vision-Language Models》 地址&#xff1a; [2312.06323]…

操作系统习题

操作系统习题 一&#xff0e;选择题二&#xff0e;判断题三&#xff0e;PV操作题 学习完操作系统以后也要做题巩固一下&#xff0c;下面是我收集到的一些题目&#xff0c;如有问题欢迎大家一起讨论&#xff01; 题目较多&#xff0c;可以选择自己需要的来做&#xff0c;答案仅供…

干货教学!!!RHEL8中ansible中常用模块的使用

内容很长各位大老爷耐心观看 本章主要介绍ansible中最常见模块的使用 文件管理模块软件包管理模块服务管理模块磁盘管理模块用户管理模块防火墙管理模块 ansible的基本用法如下 ansible 机器名 -m 模块x -a “模块的参数” 对被管理机器执行不同的操作&#xff0c;只需要调…

WinSCP显示服务器隐藏的文件

正常情况下&#xff0c;如果我们有使用WinSCP作为SFTP、FTP管理主机空间的时候&#xff0c;如果有类似.htaccess或者其他.开头或者其他特殊文件名扩展会直接看不到而是隐藏着的。这样就显得比较麻烦&#xff0c;自己都不知道有还是没有&#xff0c;比如我们要修改.htaccess伪静…

【1.8计算机组成与体系结构】磁盘管理

目录 1.磁盘基本结构与存取过程1.1 磁盘基本结构1.2 磁盘的存取过程 2.磁盘优化分布存储3.磁盘单缓冲区与双缓冲区4.磁盘移臂调度算法 1.磁盘基本结构与存取过程 1.1 磁盘基本结构 磁盘&#xff1a;柱面&#xff0c;磁道&#xff0c;扇区。 1.2 磁盘的存取过程 存取时间寻…

预测性维护在汽车制造行业中的应用

汽车制造行业是一个高度复杂和精细化的领域&#xff0c;依赖于各种设备来完成生产流程。这些设备包括机械装配线、焊接机器人、喷涂设备、传送带等。然而&#xff0c;这些设备在长时间运行中不可避免地会遇到各种故障&#xff0c;给生产进程带来延误和成本增加。为了应对这一挑…

22--Map集合

1、Map集合 现实生活与开发中&#xff0c;我们常会看到这样的一类集合&#xff1a;用户ID与账户信息、学生姓名与考试成绩、IP地址与主机名等&#xff0c;这种一一对应的关系&#xff0c;就称作映射。Java提供了专门的集合框架用来存储这种映射关系的对象&#xff0c;即java.…

安卓小练习-校园闲置交易APP(SQLite+SimpleCursorAdapter适配器)

环境&#xff1a; SDK&#xff1a;34 JDK&#xff1a;20.0.2 编写工具&#xff1a;Android Studio 2022.3.1 整体效果&#xff08;视频演示&#xff09;&#xff1a; 小练习-闲置社区APP演示视频-CSDN直播 部分效果截图&#xff1a; 整体工作流程&#xff1a; 1.用户登录&…

C语言输出菱形(详解版)

菱形&#xff0c;就是如下所示的图形&#xff0c;总行数与总列数相等&#xff1a; 写一个程序&#xff0c;根据用户输入的总行数&#xff0c;打印出菱形。 这个题目主要是找出规律&#xff0c;考察读者的逻辑思维。 你可以从第一行开始&#xff0c;遍历所有的列&#xff0c;也…

数据可视化---双Y轴折线图比较

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

Pytorch神经网络的参数管理

目录 一、参数访问 1、目标参数 2、一次性访问所有参数 3、从嵌套块收集参数 二、参数初始化 1、内置初始化 2、自定义初始化 3、参数绑定 在选择了架构并设置了超参数后&#xff0c;我们就进入了训练阶段。此时&#xff0c;我们的目标是找到使损失函数最小化的模型参数…

【漏洞复现】华为Auth-Http服务文件读取漏洞

Nx01 产品简介 Huawei Auth-HTTP Server 1.0 可以实现基于角色的访问控制&#xff0c;通过用户的身份认证和权限控制&#xff0c;确保只有经过授权的用户可以访问特定的资源和服务。它支持常见的身份认证协议和技术&#xff0c;如LDAP、RADIUS、TACACS等&#xff0c;能够与企业…

tensorflow入门 自定义模型

前面说了自定义的层&#xff0c;接下来自定义模型&#xff0c;我们以下图为例子 这个模型没啥意义&#xff0c;单纯是为了写代码实现这个模型 首先呢&#xff0c;我们看有几个部分&#xff0c;dense不需要我们实现了&#xff0c;我们就实现Res&#xff0c;为了实现那个*3,我们…

Postman使用总结--参数化

将 测试数据&#xff0c;组织到 数据文件中&#xff0c;通过脚本的反复迭代&#xff0c;使用不同的数据&#xff0c;达到测试不同用例的目标 数据文件有两种&#xff1a; CSV &#xff08;类似于excel&#xff09; 格式简单用这个 文件小 JSON&#xff08;字典列表&#x…

简单几步完成SVN的安装

介绍以及特点 SVN&#xff1a;Subversion&#xff0c;即版本控制系统。 1.代码版本管理工具 2.查看所有的修改记录 3.恢复到任何历史版本和已经删除的文件 4.使用简单上手快&#xff0c;企业安全必备 下载安装 SVN的安装分为两部分&#xff0c;第一部分是服务端安装&…

C# 图解教程 第5版 —— 第19章 枚举器和迭代器

文章目录 19.1 枚举器和可枚举类型19.2 IEnumerator 接口19.3 IEnumerable 接口19.4 泛型枚举接口19.5 迭代器19.5.1 迭代器块19.5.2 使用迭代器来创建枚举器19.5.3 使用迭代器来创建可枚举类型 19.6 常见迭代器模式19.7 产生多个可枚举类型19.8 将迭代器作为属性19.9 迭代器的…

计算机毕业设计—基于Koa+vue的高校宿舍管理系统宿舍可视化系统

项目介绍 项目背景 随着科技的发展&#xff0c;智能化管理越来越重要。大学生在宿舍的时间超过了1/3&#xff0c;因此良好的宿舍管理对学生的生活和学习极为关键。学生宿舍管理系统能够合理安排新生分配宿舍&#xff0c;不浪费公共资源&#xff0c;减轻学校管理压力&#xff…