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";@Overridepublic 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);}));}@Overridepublic 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*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 提取请求头中的traceIdString 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*/@Overridepublic 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。*/@Overridepublic 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;@Autowiredpublic TraceIdWebConfig(TraceIdInterceptor traceIdInterceptor) {this.traceIdInterceptor = traceIdInterceptor;}@Overridepublic 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";@Overridepublic 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*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 从请求头中提取traceIdString 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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt——TCP UDP网络编程

目录 前言正文一、TCP二、UDP1、基本流程2、必备知识 三、代码层级1、UDP服务端 END、总结的知识与问题1、如何获取QByteArray中某一字节的数据&#xff0c;并将其转为十进制&#xff1f;2、如何以本年本月本日为基础&#xff0c;获取时间戳&#xff0c;而不以1970为基础&#…

数字图像处理 Harris 角点和边缘检测器

一、简述 Harris角点和边缘检测器是一项古老的技术,说它古老是因为该技术从1988年被发明。下面是论文地址,技术出现的时间虽然很久,但是并不代表没有用处了,很多神经网络无法发挥作用的场景下,类似的技术还是会大行其道。 https://web.stanford.edu/class/cs231m/referen…

【SpringBoot】公共字段自动填充功能实现(枚举、自定义注解、AOP、反射)

1. 自定义注解 使用interface语法来定义注解&#xff08;Annotation&#xff09;。 注解的参数类似无参数方法&#xff0c;可以用default设定一个默认值&#xff0c;比如String value() default "";。 元注解&#xff1a;有一些注解可以修饰其他注解&#xff0c;这…

c语言-整型在内存的存储

文章目录 前言一、整型数值在内存中的存储1.1 整型数值的表示形式1.2 二进制的表示形式1.3 整数在内存中存储 二、大端字节序存储和小端字节序存储2.1 大端字节序存储2.2 小端字节序存储2.3 练习 总结 前言 本篇文章叙述c语言中整型数据在内存中的存储方式。 一、整型数值在内…

自动驾驶预测-决策-规划-控制学习(4):预测分析文献阅读

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、摘要分析1.Transformer模型是什么&#xff1f;什么是自注意力机制&#xff1f; 2.数据集是什么&#xff1f;3.预测车辆行驶轨迹和车辆换道意图4. LSTM 网络…

P12 音视频复合流——TS流讲解

前言 从本章开始我们将要学习嵌入式音视频的学习了 &#xff0c;使用的瑞芯微的开发板 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&#xff08;概念类&#xff09;_C…

MySQL之视图索引执行计划

目录 一.视图 二.执行计划 2.1.什么是执行计划 2.2.执行计划的作用 三.使用外连接、内连接和子查询进行举例 四.思维导图 好啦今天就到这里了哦&#xff01;&#xff01;&#xff01;希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.视图 含义 &#xff1a;在数…

docker部署kibana

1&#xff0c;简介 官网 kibana 2&#xff0c;安装docker 参考 linux安装docker 3&#xff0c;准备 Kibana 配置文件 # 进入主节点配置文件目录 cd /export/server/docker/kibana/config # 编辑单机版配置文件 vi kibana.ymlkibana.yml内容 # 主机地址&#xff0c;可以是…

BUUCTF--actf_2019_babyheap1

这题看名字就知道是堆题&#xff0c;先看保护&#xff1a; 保护除了PIE全开&#xff0c;黑盒测试&#xff1a; 题目提供增删查&#xff0c;没有改。看看IDA中代码逻辑&#xff1a; 逻辑跟我前面做的一题极为相似&#xff0c;就不过多分析。 这是free&#xff1a; 因为题目不能…

八大算法排序@快速排序、递归版本一(C语言版本)

目录 快速排序版本一概念算法思想一二三 快排步骤代码实现时间复杂度空间复杂度特性总结 快速排序版本一 概念 快速排序&#xff08;Quicksort&#xff09;是一种高效的排序算法&#xff0c;它是由英国计算机科学家 Tony Hoare 在1960年提出的。快速排序是基于分治&#xff08…

修改对象的行为和值(代理)

文章目录 前言一、复制一个对象二、代理对象重点来了 总结 前言 最近遇到一个需求,需要在某个位置,统一处理对象的一些属性值&#xff1a; 方案有两种: 直接复制一份,将属性覆盖后,返回一个新对象搞一个代理类,代理这个对象,修改对象的原有行为和值,从而达到修改属性值的目的…

【GoLang入门教程】Go语言几种标准库介绍(四)

编程语言的未来&#xff1f; 文章目录 编程语言的未来&#xff1f;前言几种库fmt库 (格式化操作)关键函数&#xff1a;示例 Go库标准库第三方库示例 html库(HTML 转义及模板系统)主要功能&#xff1a;示例 总结专栏集锦写在最后 前言 上一篇&#xff0c;我们介绍了debug、enco…