5. 分布式链路追踪TracingFilter改造增强设计
前言
在4. 分布式链路追踪客户端工具包Starter设计一文中,我们实现了基础的Starter包,里面提供了我们自己定义的Servlet过滤器和RestTemplate拦截器,其中Servlet过滤器叫做HoneyTracingFilter,仅提供了提取SpanContext,创建Span和开启Span的基础功能,所以本文将围绕如何增强Servlet过滤器展开讨论。
相关版本依赖如下。
opentracing-api版本:0.33.0
opentracing-spring-web版本:4.1.0
jaeger-client版本:1.8.1
Springboot版本:2.7.6
github地址:honey-tracing
正文
一. Opentracing提供的TracingFilter
其实最简单的增强方式,就是使用TracingFilter来替换我们自己实现的HoneyTracingFilter,下面给出TracingFilter的源码实现。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;if (!isTraced(httpRequest, httpResponse)) {chain.doFilter(httpRequest, httpResponse);return;}if (servletRequest.getAttribute(SERVER_SPAN_CONTEXT) != null) {chain.doFilter(servletRequest, servletResponse);} else {SpanContext extractedContext = tracer.extract(Format.Builtin.HTTP_HEADERS,new HttpServletRequestExtractAdapter(httpRequest));final Span span = tracer.buildSpan(httpRequest.getMethod()).asChildOf(extractedContext).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER).start();httpRequest.setAttribute(SERVER_SPAN_CONTEXT, span.context());for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {spanDecorator.onRequest(httpRequest, span);}try (Scope scope = tracer.activateSpan(span)) {chain.doFilter(servletRequest, servletResponse);if (!httpRequest.isAsyncStarted()) {for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {spanDecorator.onResponse(httpRequest, httpResponse, span);}}} catch (Throwable ex) {for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {spanDecorator.onError(httpRequest, httpResponse, ex, span);}throw ex;} finally {if (httpRequest.isAsyncStarted()) {httpRequest.getAsyncContext().addListener(new AsyncListener() {@Overridepublic void onComplete(AsyncEvent event) throws IOException {HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {spanDecorator.onResponse(httpRequest,httpResponse,span);}span.finish();}@Overridepublic void onTimeout(AsyncEvent event) throws IOException {HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {spanDecorator.onTimeout(httpRequest,httpResponse,event.getAsyncContext().getTimeout(),span);}}@Overridepublic void onError(AsyncEvent event) throws IOException {HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {spanDecorator.onError(httpRequest,httpResponse,event.getThrowable(),span);}}@Overridepublic void onStartAsync(AsyncEvent event) throws IOException {}});} else {span.finish();}}}
}
通过阅读TracingFilter源码,我们可以得到如下几种扩展增强。
- Servlet自身的urlPatterns机制。可以通过配置urlPatterns,决定哪些请求需要打印链路信息;
- TracingFilter的skipPattern机制。可以通过配置skipPattern,决定哪些请求不需要打印链路信息;
- 装饰器ServletFilterSpanDecorator。可以提供ServletFilterSpanDecorator给到TracingFilter,这样在收到请求,返回响应和处理异常时均可以做一些扩展操作;
二. urlPatterns和skipPattern设计
在第一节中得到的TracingFilter的几种增强,其中第1和第2点的urlPatterns和skipPattern,可以提供出来供用户配置,本节对这部分进行实现。
首先是配置属性类里面需要加入urlPatterns和skipPattern的配置属性,如下所示。
* 分布式链路追踪配置属性类。*/
@ConfigurationProperties("honey.tracing")
public class HoneyTracingProperties {private boolean enabled;private HttpUrlProperties httpUrl = new HttpUrlProperties();public boolean isEnabled() {return enabled;}public void setEnabled(boolean enabled) {this.enabled = enabled;}public HttpUrlProperties getHttpUrl() {return httpUrl;}public void setHttpUrl(HttpUrlProperties httpUrl) {this.httpUrl = httpUrl;}public static class HttpUrlProperties {* 按照/url1,/url2这样配置。*/private String urlPattern = "/*";* 按照/url1|/honey.*这样配置。*/private String skipPattern = "";public String getUrlPattern() {return urlPattern;}public void setUrlPattern(String urlPattern) {this.urlPattern = urlPattern;}public String getSkipPattern() {return skipPattern;}public void setSkipPattern(String skipPattern) {this.skipPattern = skipPattern;}}}
然后注册过滤器的配置类HoneyTracingFilterConfig需要做如下修改。
* Servlet过滤器配置类。*/
@Configuration
@AutoConfigureAfter(HoneyTracingConfig.class)
public class HoneyTracingFilterConfig {@Autowiredprivate HoneyTracingProperties honeyTracingProperties;@Beanpublic FilterRegistrationBean<TracingFilter> honeyTracingFilter(Tracer tracer) {String urlPattern = honeyTracingProperties.getHttpUrl().getUrlPattern();String skipPatternStr = honeyTracingProperties.getHttpUrl().getSkipPattern();Pattern skipPattern = Pattern.compile(skipPatternStr);FilterRegistrationBean<TracingFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.addUrlPatterns(urlPattern);registrationBean.setOrder(Integer.MIN_VALUE);registrationBean.setFilter(new TracingFilter(tracer, new ArrayList<>(), skipPattern));return registrationBean;}}
三. TracingFilter的装饰器设计
通过为TracingFilter注册ServletFilterSpanDecorator装饰器,可以让我们在收到请求,返回响应和处理异常时做一些扩展操作,例如记录请求url,api和返回码等,下面实现一个装饰器HoneyServletFilterSpanDecorator,其提供如下几个功能。
收到请求时记录:
- 请求的host;
- 请求的api。
返回响应时记录:
- 响应码。
处理异常时记录:
- 响应码。
实现如下。
* {@link TracingFilter}的装饰器。*/
public class HoneyServletFilterSpanDecorator implements ServletFilterSpanDecorator {@Overridepublic void onRequest(HttpServletRequest httpServletRequest, Span span) {span.setTag(FIELD_HOST, getHostFromRequest(httpServletRequest));span.setTag(FIELD_API, httpServletRequest.getRequestURI());}@Overridepublic void onResponse(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Span span) {span.setTag(FIELD_HTTP_CODE, httpServletResponse.getStatus());}@Overridepublic void onError(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Throwable exception, Span span) {span.setTag(FIELD_HTTP_CODE, httpServletResponse.getStatus());}@Overridepublic void onTimeout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, long timeout, Span span) {}private String getHostFromRequest(HttpServletRequest httpServletRequest) {return httpServletRequest.getScheme()+ "://"+ httpServletRequest.getServerName()+ ":"+ httpServletRequest.getServerPort();}}
相关的常量字段记录在CommonConstants中,如下所示。
public class CommonConstants {public static final double DEFAULT_SAMPLE_RATE = 1.0;public static final String HONEY_TRACER_NAME = "HoneyTracer";public static final String HONEY_REST_TEMPLATE_NAME = "HoneyRestTemplate";public static final String FIELD_HOST = "host";public static final String FIELD_API = "api";public static final String FIELD_HTTP_CODE = "httpCode";}
在注册TracingFilter时需要将HoneyServletFilterSpanDecorator设置给TracingFilter,对应的配置类HoneyTracingFilterConfig修改如下。
* Servlet过滤器配置类。*/
@Configuration
@AutoConfigureAfter(HoneyTracingConfig.class)
public class HoneyTracingFilterConfig {@Autowiredprivate HoneyTracingProperties honeyTracingProperties;@Beanpublic FilterRegistrationBean<TracingFilter> honeyTracingFilter(Tracer tracer,List<ServletFilterSpanDecorator> decorators) {String urlPattern = honeyTracingProperties.getHttpUrl().getUrlPattern();String skipPatternStr = honeyTracingProperties.getHttpUrl().getSkipPattern();Pattern skipPattern = Pattern.compile(skipPatternStr);FilterRegistrationBean<TracingFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.addUrlPatterns(urlPattern);registrationBean.setOrder(Integer.MIN_VALUE);registrationBean.setFilter(new TracingFilter(tracer, decorators, skipPattern));return registrationBean;}@Beanpublic ServletFilterSpanDecorator honeyServletFilterSpanDecorator() {return new HoneyServletFilterSpanDecorator();}}
至此,我们就使用装饰器装饰了TracingFilter,效果就是最终在TracingFilter调用到Span的finish() 方法时,我们可以从Span的tags中拿到本次请求的host,api和httpCode,这些数据可以最终在打印链路日志时使用。
最后给出工程目录结构图。

总结
本文在4. 分布式链路追踪客户端工具包Starter设计的基础上,使用TracingFilter替换了我们自己实现的HoneyTracingFilter,并且基于urlPatterns,skipPattern和装饰器进行了扩展增强。
相关文章:
5. 分布式链路追踪TracingFilter改造增强设计
前言 在4. 分布式链路追踪客户端工具包Starter设计一文中,我们实现了基础的Starter包,里面提供了我们自己定义的Servlet过滤器和RestTemplate拦截器,其中Servlet过滤器叫做HoneyTracingFilter,仅提供了提取SpanContext࿰…...
C++数据类型与表达式
一 C中的数据类型 二 基本数据类型 三 类型转换 各种类型的高低顺序如下所述; 四 构造数据类型 类类型...
电脑ip地址设置成什么比较好
随着信息技术的快速发展,IP地址已成为电脑在网络世界中的“身份证”。它不仅是电脑在网络中进行通信的基础,也直接关系到网络连接的稳定性、安全性和效率。然而,面对众多IP地址设置选项,许多用户可能会感到困惑。那么,…...
vue-element-template优化升级dart-sass、pnpm
1、替换 node-sass 为 dart-sass - "node-sass": "^4.9.0","sass": "^1.75.0",替换css深度作用域写法 /deep/ >>># 替换为::v-deepVue:Node Sass VS. Dart Sass 2、替换npm为pnpm,需要补充一些依赖…...
Oracle拼接json字符串
在Oracle数据库中,并没有内建的JSON处理函数像其他现代数据库那样直接。但是,你可以使用字符串连接和格式化技巧来拼接JSON字符串。 以下是一个简单的例子,说明如何在Oracle中拼接一个JSON字符串: sql DECLARE v_json_string V…...
如何向Linux内核提交开源补丁?
2021年,我曾经在openEuler社区上看到一项改进Linux内核工具的需求,因此参与过Linux内核社区的开源贡献。贡献开源社区的流程都可以在内核社区文档中找到,但是,单独学习需要一个较长的过程,新手难以入门,因此…...
python数据分析——pandas DataFrame基础知识2
参考资料:活用pandas库 1、分组方式 我们可以把分组计算看作“分割-应用-组合”(split-apply-combine)的过程。首先把数据分割成若干部分,然后把选择的函数(或计算)应用于各部分,最后把所有独立…...
TODESK远程开机的原理
在现代计算机技术飞速发展的背景下,远程控制软件成为我们日常工作中不可或缺的工具。其中,ToDesk作为一款高效且易用的远程控制软件,备受用户青睐。那么,ToDesk远程开机的原理是什么呢?本文将为你揭晓这个秘密。 KKVie…...
【c1】数据类型,运算符/循环,数组/指针,结构体,main参数,static/extern,typedef
文章目录 1.数据类型:编译器(compiler)与解释器(interpreter),中文里的汉字和标点符号是两个字节,不能算一个字符(单引号)2.运算符/循环:sizeof/size_t3.数组…...
word图片水印
一、word中旧水印如何删除 打开word模板,想要删除旧水印,如下图所示操作,但是旧水印删除不掉。 以为上传新水印图片会替换掉旧水印,结果显示了2个水印,要怎么删除呢? 如下截图所示,双击打开页…...
kali安装及替换源
一、安装及简单配置 1.安装:地址就不贴了,自己打一下就好 2.虚拟机中打开kali 3.替换包源 (1)使用指令打开/etc/apt/sources.list mousepad /etc/apt/sources.list (2)将内容替换成阿里云源 deb http://mirrors.aliyun.com/kali kali-rolling main n…...
JSpdf,前端下载大量表格数据pdf文件,不创建dom
数据量太大使用dom》canvas》image》pdf.addimage方法弊端是canvas超出 浏览器承受像素会图片损害,只能将其切割转成小块的canvas,每一次调用html2canvas等待时间都很长累积时间更长,虽然最终可以做到抽取最小dom节点转canvas拼接数据,但是死…...
PHP关联数组[区别,组成,取值,遍历,函数]
关联数组 相较于数值数组,关联数组的索引可以为字符串和数字,关联数组元素也可称为键值对,索引为键,值为值。 源码 <?php echo "<hr>"; //水平线标签//关联数组$arr3 array(); //创建空的数组//关联数…...
JavaWeb--13Mybatis(2)
Mybatis(2) 1 Mybatis基础操作1.1 需求和准备工作1.2 删除员工日志输入参数占位符 1.3 新增员工1.4 修改员工信息1.5 查询员工1.5.1 根据ID查询数据封装 1.5.3 条件查询 2 XML配置文件规范3 MyBatis动态SQL3.1 什么是动态SQL3.2 动态SQL-if更新员工 3.3 …...
如何远程控制另一部手机:远程控制使用方法
在现今高科技的社会中,远程控制手机的需求在某些情境下变得越来越重要。不论是为了协助远在他乡的家人解决问题,还是为了确保孩子的在线安全,了解如何实现这一功能都是有益的。本文将为您简要介绍几种远程控制手机的方法及其使用要点。 KKVi…...
x64dbg中类似于*.exe+地址偏移
在CE和xdb中,形如*.exe数字偏移形式的地址被称为模块地址,CE附加到进程后点击查看内存,显示如下图 这种地址学名叫做模块地址,在x64dbg中显示如下图: CE中可以关闭,从而显示绝对的虚拟地址,如下…...
ICode国际青少年编程竞赛- Python-1级训练场-基础训练1
ICode国际青少年编程竞赛- Python-1级训练场-基础训练1 1、 Dev.step(4)2、 Dev.step(-4) Dev.step(8)3、 Dev.turnLeft() Dev.step(4)4、 Dev.step(3) Dev.turnLeft() Dev.step(-1) Dev.step(4)5、 Dev.step(-1) Dev.step(3) Dev.step(-2) Dev.turnLeft() Dev.step(…...
Baidu Comate智能编码助手
Baidu Comate智能编码助手 🎈1.Baidu Comate的简介🎈2.安装Baidu Comate🎈3.Baidu Comate实现功能🎈4.使用注释进行智能代码提示🎈5.结束语 🎈1.Baidu Comate的简介 根据官网的介绍,我们了解到B…...
nginx自动部署-跨操作系统
项目里面有一个需求,就是需要用让nginx进程提供给系统管理一个start,stop和getPid方法,这样系统管理可以自动拉起来nginx,达到自动部署的目的。离线部署同样适用 这样一来,我就需要提供windows版本linux不同版本的nginx源码包&am…...
组合模式(结构型)
目录 一、前言 二、透明组合模式 三、安全组合模式 四、总结 一、前言 组合模式(Composite Pattern)是一种结构型设计模式,将对象组合成树形结构以表示“部分-整体”得层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 组合模式由以下角色组成…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
