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() {
                            @Override
                            public 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();
                            }

                            @Override
                            public 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);
                                }
                            }

                            @Override
                            public 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);
                                }
                            }

                            @Override
                            public void onStartAsync(AsyncEvent event) throws IOException {
                            }
                        });
            } else {
                
                span.finish();
            }
        }
    }
}

通过阅读TracingFilter源码,我们可以得到如下几种扩展增强。

  1. Servlet自身的urlPatterns机制。可以通过配置urlPatterns,决定哪些请求需要打印链路信息;
  2. TracingFilterskipPattern机制。可以通过配置skipPattern,决定哪些请求不需要打印链路信息;
  3. 装饰器ServletFilterSpanDecorator。可以提供ServletFilterSpanDecorator给到TracingFilter,这样在收到请求,返回响应和处理异常时均可以做一些扩展操作;

二. urlPatterns和skipPattern设计

在第一节中得到的TracingFilter的几种增强,其中第1和第2点的urlPatternsskipPattern,可以提供出来供用户配置,本节对这部分进行实现。

首先是配置属性类里面需要加入urlPatternsskipPattern的配置属性,如下所示。


 * 分布式链路追踪配置属性类。
 */
@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 {

    @Autowired
    private HoneyTracingProperties honeyTracingProperties;

    @Bean
    public 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装饰器,可以让我们在收到请求,返回响应和处理异常时做一些扩展操作,例如记录请求urlapi和返回码等,下面实现一个装饰器HoneyServletFilterSpanDecorator,其提供如下几个功能。

收到请求时记录:

  1. 请求的host
  2. 请求的api

返回响应时记录:

  1. 响应码。

处理异常时记录:

  1. 响应码。

实现如下。


 * {@link TracingFilter}的装饰器。
 */
public class HoneyServletFilterSpanDecorator implements ServletFilterSpanDecorator {

    @Override
    public void onRequest(HttpServletRequest httpServletRequest, Span span) {
        span.setTag(FIELD_HOST, getHostFromRequest(httpServletRequest));
        span.setTag(FIELD_API, httpServletRequest.getRequestURI());
    }

    @Override
    public void onResponse(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Span span) {
        span.setTag(FIELD_HTTP_CODE, httpServletResponse.getStatus());
    }

    @Override
    public void onError(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Throwable exception, Span span) {
        span.setTag(FIELD_HTTP_CODE, httpServletResponse.getStatus());
    }

    @Override
    public 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 {

    @Autowired
    private HoneyTracingProperties honeyTracingProperties;

    @Bean
    public 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;
    }

    @Bean
    public ServletFilterSpanDecorator honeyServletFilterSpanDecorator() {
        return new HoneyServletFilterSpanDecorator();
    }

}

至此,我们就使用装饰器装饰了TracingFilter,效果就是最终在TracingFilter调用到Spanfinish() 方法时,我们可以从Spantags中拿到本次请求的hostapihttpCode,这些数据可以最终在打印链路日志时使用。

最后给出工程目录结构图。

总结

本文在4. 分布式链路追踪客户端工具包Starter设计的基础上,使用TracingFilter替换了我们自己实现的HoneyTracingFilter,并且基于urlPatternsskipPattern和装饰器进行了扩展增强。

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

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

相关文章

等保测评技术方案

等保&#xff0c;即“网络安全等级保护”&#xff0c;是中国实施的一项信息安全保护制度&#xff0c;旨在对不同重要性的信息和信息系统实行分等级保护&#xff0c;保障国家安全、社会秩序以及公共利益。等保技术方案是指为了达到国家网络安全等级保护标准要求&#xff0c;针对…

Linux的并发与竞争

文章目录 一、并发二、竞争三、保护内容是什么四、解决并发与竞争的几种常用方法1.原子操作原子整型API函数原子位操作 API 函数 2.自旋锁自旋锁格式如下&#xff1a;自旋锁 API 函数自旋锁的使用注意事项 3.信号量信号量 API 函数信号量格式如下&#xff1a; 4.互斥体API函数如…

正交频分复用回顾(通俗易懂)

OFDM我们知道&#xff0c;叫做正交频分复用&#xff0c;它是4G的一个关键技术&#xff0c;4G的多址技术叫做OFDMA&#xff0c;也就是说4G是通过OFDM来作用户区分的&#xff0c;具体是什么意思呢&#xff1f;继续往下看。 图1 在2G和3G时代&#xff0c; 单用户都是用的一个载波…

进口原装二手 Keysight86142B 是德86142A 高性能光谱分析仪

进口原装二手 Keysight86142B 是德86142A 高性能光谱分析仪 内置测试应用程序 • 10 pm 波长精度 • 快速双扫法 • 覆盖 S、C 和 L 波段 Keysight 86142B是一款台式光谱分析仪&#xff08;OSA&#xff09;&#xff0c;最适于对功率和波长精度、动态范围和低偏振敏感性都要…

深入理解Linux中TCP/IP协议栈的实现原理与具体过程

一、Linux内核与网络体系结构 在我们了解整个linux系统的网络体系结构之前&#xff0c;我们需要对整个网络体系调用&#xff0c;初始化和交互的位置&#xff0c;同时也是Linux操作系统中最为关键的一部分代码-------内核&#xff0c;有一个初步的认知。 1、Linux内核的结构 …

ansible离线部署etcd二进制集群

目录 概述资源安装执行过程集群验证 概述 功能如下&#xff1a; ansible 2.9版本离线安装centos 7 内核离线升级cfssl 离线二进制安装etcd 3.5.13版本 二进制离线安装 资源 相关前置资源如下 资源地址Ansible离线安装地址ansible-playbook离线升级centos内核地址ansible离线…

基于springboot实现社区医院管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现社区医院管理系统演示 摘要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&…

MySQL——变量的定义与使用

新建链接&#xff0c;自带world数据库&#xff0c;里面自带city表格。 DQL # MySQL变量的定义与使用 #1、不允许数字作为开头 #2、只能用_或$符号&#xff0c;不允许使用其他符号 #3、不允许使用关键字或保留字 set userName小可爱; select userName; #标识符只影响当前查询#…

[C++]哈希应用-海量数据处理

文章目录 海量数据处理前言哈希切分问题1&#xff1a;给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址&#xff1f;问题2&#xff1a;给一个超过100G大小的log file, log中存着IP地址, 设计算法找到top K的IP&#xff1f; 位图应用问题3&…

UART、SPI 与 I2C:走线和布局指南

这是翻译自PCB Hero的一篇非常基础的文章。 还有一篇关于这三个总线的比较文章可以参照阅读一下:https://www.totalphase.com/blog/2021/12/i2c-vs-spi-vs-uart-introduction-and-comparison-similarities-differences/ I2C、SPI、UART 之间的差异及其布局指南 从8位到32位的…

ECP44304T-76是一款增强型通信处理器吗?

ABB ECP44304T-76是一款增强型通信处理器&#xff0c;专为ABB的PLC控制系统设计。 这款通信处理器的主要功能是提供PLC与其他设备或网络之间的通信接口。它支持多种通讯协议&#xff0c;包括但不限于Profibus、Ethernet、Modbus等&#xff0c;使得PLC可以轻松集成到复杂的工业…

【最大公约数 唯一分解定理 调和级数】2862. 完全子集的最大元素和

本文涉及知识点 质数、最大公约数、菲蜀定理 组合数学汇总 唯一分解定理 调和级数 LeetCode2862. 完全子集的最大元素和 给你一个下标从 1 开始、由 n 个整数组成的数组。你需要从 nums 选择一个 完全集&#xff0c;其中每对元素下标的乘积都是一个 完全平方数&#xff0c;例…

程序员学CFA——数量分析方法(六)

数量分析方法&#xff08;六&#xff09; 假设检验假设检验的步骤假设检验的基本思想与步骤估计与假设检验的区别假设检验的基本思想假设检验的步骤 假设检验的相关概念原假设与备择假设检验统计量及其分布显著性水平双尾检验与单尾检验p值第一类错误与第二类错误统计显著与经济…

力扣HOT100 - 155. 最小栈

解题思路&#xff1a; 辅助栈 class MinStack {private Stack<Integer> stack;private Stack<Integer> min_stack;public MinStack() {stack new Stack<>();min_stack new Stack<>();}public void push(int val) {stack.push(val);if (min_stack.i…

SpringBoot集成jxls2实现复杂(多表格)excel导出

核心依赖 需求 导出多个表格&#xff0c;包含图片&#xff0c;类似商品标签 1.配置模板 创建一个xlsx的模板文件&#xff0c;配置如下 该模板进行遍历了两次&#xff0c;因为我想要导出的数据分为两列展示&#xff0c;左右布局&#xff0c;一个循环实现不了&#xff0c;所以采…

计算机系列之面向对象、设计模式

24、面向对象技术&#xff08;重要&#xff0c;10分左右&#xff09; 1、面向对象开发 (1)对象:由数据及其操作所构成的封装体&#xff0c;是系统中用来描述客观事务的个实体&#xff0c;是构成系统的一个基本单位。一个对象通常可以由对象名、属性和方法3个部分组成。 (2)类…

YOLOV5更换转置卷积,助力涨点!

由于转置卷积是nn库自带的,所以我们直接找到models文件夹中的yolo.py文件中的 parse_model函数,再在如下图的地方添加转置卷积模块 # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license """ YOLO-specific modules.Usage:$ python models/yolo.py --cfg yolov5s.…

ARM 交叉编译搭建SSH

一、源码下载 zlib&#xff1a;zlib-1.3.1.tar.xz openssl&#xff1a;openssl-0.9.8d.tar.gz openssh&#xff1a;openssh-4.6p1.tar.gz 二、交叉编译 1、zlib 编译参考这里 2、openssl tar -xf openssl-0.9.8d.tar.gz ./Configure --prefix/opt/ssh/openssl os/compile…

一对一WebRTC视频通话系列(五)——综合调试和功能完善

本系列博客主要记录一对一WebRTC视频通话实现过程中的一些重点&#xff0c;代码全部进行了注释&#xff0c;便于理解WebRTC整体实现。 本专栏知识点是通过<零声教育>的音视频流媒体高级开发课程进行系统学习&#xff0c;梳理总结后写下文章&#xff0c;对音视频相关内容感…

猿匹配,一款使用环信实现的一个开源聊天应用含服务器

前言 之前写了一篇Android开发集成聊天环信SDK3.x简单开始&#xff0c;然后最近得空开发了一款使用环信实现的实时聊天应用&#xff0c;包含简单的服务器端&#xff0c;并开源给大家&#xff0c;有兴趣的同学可以一起搞一下&#xff0c;详细介绍看下边吧 上代码 服务器&#…