Java中请求生成唯一追溯TraceId

Java中请求生成唯一追溯TraceId

一:背景

因为是微服务架构,平常日志太多,看日志不太好查,所以想要从一整个链路当中获取一个唯一标识,比较好定位问题,

原理就是从gateway网关将标识传递到下游,下游服务拿到这个标识,响应结束后将traceId反向写入响应体中

二:具体实现

例如我现在有3个微服务,gateway,center-service,注册中心;一个接口完整的请求链路是从gateway路由到center-service中。

1、Gateway

网关处理请求唯一标识

package com.wondertek.gateway.traceId;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.UUID;

@Component
@Slf4j
public class TraceIdResponseFilter implements GlobalFilter, Ordered {

    private static final String TRACE_ID_HEADER = "X-Trace-Id";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 在请求前确保traceId存在
        List<String> existingTraceIds = exchange.getRequest().getHeaders().get(TRACE_ID_HEADER);
        String traceId = existingTraceIds != null ? existingTraceIds.get(0) : UUID.randomUUID().toString().replaceAll("-","");
        log.info("网关传递到下游服务的traceId值为:{}", traceId);
        exchange.getRequest().mutate().header(TRACE_ID_HEADER, traceId).build();
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            // 在响应后添加traceId到响应头
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().add(TRACE_ID_HEADER, traceId);
        }));
    }

    @Override
    public int getOrder() {
        // 响应后执行
        return Ordered.LOWEST_PRECEDENCE;
    }
}

2、center-service

得将此拦截器注册到您的MVC配置中

package com.wondertek.web.traceId;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class TraceIdInterceptor implements HandlerInterceptor {
    
    private static final String TRACE_ID_HEADER = "X-Trace-Id";

    /**
     * 这个方法在请求处理之前被调用。在这里,它试图从请求头中获取名为 X-Trace-Id 的值,这通常是一个用于追踪请求在不同微服务之间流转的唯一标识符。
     * 如果这个标识符存在,它会被保存在请求的属性中,这样在后续的处理流程中可以继续访问和使用这个 traceId。
     * @param request
     * @param response
     * @param handler
     * @return
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 提取请求头中的traceId
        String traceId = request.getHeader(TRACE_ID_HEADER);
        // 如果traceId存在,则将其存储到请求中供后续使用
        if (traceId != null) {
            request.setAttribute(TRACE_ID_HEADER, traceId);
        }
        return true;
    }

    /**
     * 这个方法在请求处理之后被调用,但在视图渲染之前执行。
     * 在这里,它从请求属性中获取之前存储的 traceId,然后将这个 traceId 设置在响应头中。
     * 这样做保证了,如果请求中包含了 traceId,那么这个 traceId 将会随着响应返回给发起请求的客户端。
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        // 将traceId设置到响应头中,确保返回给调用者
        Object traceId = request.getAttribute(TRACE_ID_HEADER);
        if (traceId != null) {
            response.setHeader(TRACE_ID_HEADER, traceId.toString());
        }
    }

    // 其他方法根据需要实现
    /**
     * afterCompletion方法是Spring MVC的 HandlerInterceptor 接口中的一个回调函数,它在整个请求处理完毕后执行,也就是在视图渲染完毕且响应已经被发送给客户端之后。
     * 这个方法会在请求的最后阶段被调用,通常用于清理资源,记录日志,进行异常处理等操作。
     * 这个方法提供了四个参数:
     * HttpServletRequest:当前的请求对象。
     * HttpServletResponse:当前的响应对象。
     * Object:处理器(通常是一个控制器)。
     * Exception:执行处理器过程中抛出的异常。如果没有异常抛出则为null。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        if (ex != null) {
            // 日志中记录异常信息
            log.error("请求处理发生错误,url:{}", ex,request.getServletPath());
        } else {
            // 日志中记录请求处理成功
            log.info("请求处理成功,url:{}",request.getServletPath());
        }
        // 这里还可以根据需要进行其他操作
    }

}

将此拦截器注册到您的MVC配置中

package com.wondertek.web.traceId;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class TraceIdWebConfig implements WebMvcConfigurer {

    private final TraceIdInterceptor traceIdInterceptor;

    @Autowired
    public TraceIdWebConfig(TraceIdInterceptor traceIdInterceptor) {
        this.traceIdInterceptor = traceIdInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(traceIdInterceptor);
    }
}

响应请求体中反写traceId

package com.wondertek.web.traceId;

import com.wondertek.web.exception.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.web.bind.annotation.ControllerAdvice;

import java.util.UUID;

@ControllerAdvice
@Slf4j
public class TraceIdResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final String TRACE_ID_HEADER = "X-Trace-Id";

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //如果只想处理返回类型为Result的控制器方法,您可以在supports方法中做出相应的判断:
        // return returnType.getParameterType().equals(Result.class);

        // 可以进一步细化条件,只处理Result类型的响应

        //这个supports方法的实现总是返回true,这意味着无论返回类型或者转换器类型是什么,当前的ResponseBodyAdvice实现都将被用于所有的响应。
        return true;
    }

    /**
     * 此代码段是ResponseBodyAdvice接口中beforeBodyWrite方法的一个具体实现。此方法允许您在响应体被发送给客户端前对其进行修改。
     * @param body
     * @param returnType
     * @param selectedContentType
     * @param selectedConverterType
     * @param request
     * @param response
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 从请求头中提取traceId
        String traceId = request.getHeaders().getFirst(TRACE_ID_HEADER);
        if (body instanceof Result) {
            Result<?> result = (Result<?>) body;
            if (traceId != null) {
                log.info("下游服务接收到的traceId值为:{}", traceId);
                result.setTraceId(traceId);
            } else {
                // 如果服务从请求头中获取不到traceId,生成一个新的
                traceId = UUID.randomUUID().toString().replaceAll("-", "");
                //log.info("下游服务生成的traceId值为:{}", traceId);
                result.setTraceId(traceId);
            }
        }
        return body;
    }
}

Result为自己定义的全局返回对象,其中要加入traceId

image-20240105101009657

image-20240105101208037

三:实现效果

image-20240105100450724image-20240105100530070

image-20240105100556152

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

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

相关文章

C# Onnx Chinese CLIP 通过一句话从图库中搜出来符合要求的图片

目录 效果 生成图片特征 查找踢足球的人 测试图片 模型信息 image_model.onnx text_model.onnx 项目 代码 Form1.cs Clip.cs 下载 C# Onnx Chinese CLIP 通过一句话从图库中搜出来符合要求的图片 效果 生成图片特征 查找踢足球的人 测试图片 模型信息 image_mod…

RFM会员价值度模型

模型基本原理 会员价值度用来评估用户的价值情况&#xff0c;是区分会员价值的重要模型和参考依据&#xff0c;也是衡量不同营销效果的关键指标。 价值度模型一般基于交易行为产生&#xff0c;衡量的是有实体转化价值的行为。常用的价值度模型是RFM RFM模型是根据会员 最近…

如何在 Windows 上从电脑硬盘恢复照片

如今&#xff0c;随着相机设备的普及&#xff0c;您可以轻松地一次拍摄一堆照片&#xff0c;将它们传输到硬盘上&#xff0c;然后再拍摄更多照片。但是&#xff0c;如果您的所有照片意外丢失在驱动器中怎么办&#xff1f;你能恢复它们吗&#xff1f; 在本指南中&#xff0c;我…

配置文件的创建和部署

配置描述文件&#xff08;Configuration Profiles&#xff09;的格式为xml&#xff0c;其提供了一个非常容易的方式去给电脑、移动设备或用户定义一些设置或限制&#xff0c;你可以使用Jamf Pro去创建这样的配置文件。 &#xff08;配置文件的负载&#xff09; 有关配置文件的配…

Spring——Spring AOP1(代理模式Proxy)

代理&#xff08;Proxy&#xff09;模式 1.创建工程 2.代理&#xff08;Proxy&#xff09;模式介绍 作用&#xff1a;通过代理可以控制访问某个对象的方法&#xff0c;在调用这个方法前做前置处理&#xff0c;调用这个方法后做后置处理。&#xff08;即&#xff1a; AOP的微观…

Postman 并发测试入门指南:如何模拟用户并发请求?

背景介绍 最近&#xff0c;我们发起了一个在线图书管理系统的项目。我负责的一个关键模块包括三个主要后台接口&#xff1a; 实现对books数据的检索。实施对likes数据的获取。通过collections端点访问数据。 应对高流量的挑战 在设计并部署接口时&#xff0c;我们不可避免地…

Anaconda安装教程及注意事项

一、Anaconda简介 Anaconda是一个大数据处理、科学计算领域的Python发行版。它包含了众多流行的开源软件和库&#xff0c;如NumPy、SciPy、Matplotlib、Pandas等&#xff0c;并同时提供了一个方便的打包、分发和管理这些工具的工具集合&#xff0c;如conda和pip等。Anaconda还提…

Service Weaver:Google开源基于分布式应用程序开发的框架,重新定义微服务边界

大家好&#xff0c;我是萧楚河&#xff0c;公众号&#xff1a;golang面试经典讲解&#xff0c;感谢关注&#xff0c;一起学习一起成长。一、前言 今年6月&#xff0c;一群谷歌员工&#xff08;由谷歌软件工程师Michael Whittaker领导&#xff09;发表了一篇名为“Towards Mode…

transforms图像增强(一)

一、数据增强 数据增强&#xff08;Data Augmentation&#xff09;是一种常用的数据预处理技术&#xff0c;通过对训练集进行各种变换和扩增操作&#xff0c;可以增加训练数据的多样性和丰富性&#xff0c;从而提高模型的泛化能力。 数据增强的目的是通过对训练集中的图像进行…

如何在Ubuntu安装SVN服务并结合cpolar实现公网TCP地址远程访问本地服务

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

locust 快速入门--程序调试

背景 对测试的api引入locust后&#xff0c;不在使用requests库进行http请求了&#xff0c;而是通过client属性发送请求&#xff0c;实质是使用HttpSession。 问题&#xff1a;如果对locust程序进行调试 解决方案&#xff1a; 因为locust使用协程&#xff0c;需要开启pych…

剧本杀小程序/APP搭建,增加玩家游戏体验

近年来&#xff0c;剧本杀游戏成为了年轻人娱乐的新方式&#xff0c;受到了年轻人的追捧。 剧本杀是一种新型的社交游戏&#xff0c;在游戏中&#xff0c;玩家不仅可以进行角色扮演&#xff0c;也能够交到好友&#xff0c;符合当下年轻人的生活模式。 小程序、app是当下剧本杀…

微服务-java spi 与 dubbo spi

Java SPI 通过一个案例来看SPI public interface DemoSPI {void echo(); } public class FirstImpl implements DemoSPI{Overridepublic void echo() {System.out.println("first echo");} } public class SecondImpl implements DemoSPI{Overridepublic void ech…

C++ 类的内存分布

文章目录 1 . 前言2 . 无继承&#xff0c;无虚函数3 . 无继承&#xff0c;有虚函数4 . 单一继承&#xff0c;无虚函数5 . 单一继承&#xff0c;有虚函数&#xff0c;虚析构6 . 多重继承7 . 菱形继承8 . 虚拟继承9 . 总结 【极客技术传送门】 : https://blog.csdn.net/Engineer_…

Vue框架底层

一、前端框架的由来 1、服务端渲染 sequenceDiagram 浏览器->>服务器: https://www.bilibili.com/ Note right of 服务器: 组装页面(服务端渲染) 服务器->>-浏览器: 完整页面2、前后端分离 sequenceDiagram 浏览器->>服务器: https://www.bilibili.com/ 服务…

算法基础之合并果子

合并果子 核心思想&#xff1a; 贪心 Huffman树(算法): 每次将两个最小的堆合并 然后不断向上合并 #include<iostream>#include<algorithm>#include<queue> //用小根堆实现找最小堆using namespace std;int main(){int n;cin>>n;priority_queue&l…

支持 input 函数的在线 python 运行环境 - 基于队列

支持 input 函数的在线 python 运行环境 - 基于队列 思路两次用户输入三次用户输入 实现前端使用 vue element uiWindows 环境的执行器子进程需要执行的代码 代码仓库参考 本文提供了一种方式来实现支持 input 函数&#xff0c;即支持用户输的在线 python 运行环境。效果如下图…

查询json数组

步骤一&#xff1a;创建表格 首先&#xff0c;我们需要创建一个表格来存储包含JSON对象数组的数据。可以使用以下代码创建一个名为 my_table 的表格&#xff1a; CREATE TABLE my_table (id INT PRIMARY KEY AUTO_INCREMENT,json_data JSON ); 上述代码创建了一个包含两个列的…

算法实验T15——POJ1636 Prison rearrangement

题目描述 Prison rearrangement Time Limit: 3000MSMemory Limit: 10000KTotal Submissions: 6415Accepted: 2718 Description&#xff1a; In order to lower the risk of riots and escape attempts, the boards of two nearby prisons of equal prisoner capacity, have dec…

计算机毕业设计 SpringBoot的中小型制造企业质量管理系统 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…