网站建设网站建设 网站制作web网站模板
在分布式系统中,traceId
需要在服务之间传递,以确保整个请求链路的日志能够被追踪。下面我们通过一个具体的例子,展示如何通过 A 服务调用 B 服务时传递 traceId
,并确保日志中能够正确记录 traceId
。
实现思路
-
A 服务生成
traceId
:在 A 服务的HandlerInterceptor
中生成traceId
,并将其放入MDC
。 -
A 服务调用 B 服务时传递
traceId
:在 A 服务调用 B 服务时,将traceId
通过 HTTP 头传递给 B 服务。 -
B 服务接收
traceId
:在 B 服务中,通过HandlerInterceptor
或过滤器从 HTTP 头中提取traceId
,并将其放入MDC
。 -
日志输出
traceId
:在 A 服务和 B 服务的日志配置中,确保traceId
能够被输出。
代码实现
1. A 服务代码
(1) TraceIdInterceptor
生成 traceId
java
复制
import org.slf4j.MDC; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID;@Component public class TraceIdInterceptor implements HandlerInterceptor {private static final String TRACE_ID = "traceId";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 生成唯一的 traceIdString traceId = UUID.randomUUID().toString();// 将 traceId 放入 MDCMDC.put(TRACE_ID, traceId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 请求完成后清理 MDC 中的 traceIdMDC.remove(TRACE_ID);} }
(2) 注册 TraceIdInterceptor
java
复制
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 WebConfig implements WebMvcConfigurer {@Autowiredprivate TraceIdInterceptor traceIdInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(traceIdInterceptor);} }
(3) 调用 B 服务时传递 traceId
在 A 服务中,使用 RestTemplate
或 Feign
调用 B 服务时,将 traceId
添加到 HTTP 头中。
java
复制
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;import java.net.URI;@RestController public class AServiceController {private static final Logger logger = LoggerFactory.getLogger(AServiceController.class);private final RestTemplate restTemplate = new RestTemplate();@GetMapping("/call-b-service")public String callBService() {// 获取当前请求的 traceIdString traceId = MDC.get("traceId");logger.info("A Service - Calling B Service with traceId: {}", traceId);// 设置 HTTP 头,传递 traceIdHttpHeaders headers = new HttpHeaders();headers.add("X-Trace-Id", traceId);// 调用 B 服务RequestEntity<Void> request = new RequestEntity<>(headers, HttpMethod.GET, URI.create("http://localhost:8081/b-service"));ResponseEntity<String> response = restTemplate.exchange(request, String.class);return "A Service received response from B Service: " + response.getBody();} }
2. B 服务代码
(1) TraceIdFilter
接收 traceId
在 B 服务中,使用过滤器从 HTTP 头中提取 traceId
,并将其放入 MDC
。
java
复制
import org.slf4j.MDC; import org.springframework.stereotype.Component;import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.UUID;@Component public class TraceIdFilter implements Filter {private static final String TRACE_ID = "traceId";@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;// 从 HTTP 头中获取 traceIdString traceId = httpRequest.getHeader("X-Trace-Id");if (traceId == null || traceId.isEmpty()) {// 如果头中没有 traceId,生成一个新的traceId = UUID.randomUUID().toString();}// 将 traceId 放入 MDCMDC.put(TRACE_ID, traceId);try {chain.doFilter(request, response);} finally {// 请求完成后清理 MDC 中的 traceIdMDC.remove(TRACE_ID);}}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void destroy() {} }
(2) 注册 TraceIdFilter
java
复制
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class FilterConfig {@Beanpublic FilterRegistrationBean<TraceIdFilter> traceIdFilter() {FilterRegistrationBean<TraceIdFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new TraceIdFilter());registrationBean.addUrlPatterns("/*");return registrationBean;} }
(3) B 服务接口
java
复制
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController public class BServiceController {private static final Logger logger = LoggerFactory.getLogger(BServiceController.class);@GetMapping("/b-service")public String bService() {// 获取当前请求的 traceIdString traceId = MDC.get("traceId");logger.info("B Service - Processing request with traceId: {}", traceId);return "Hello from B Service!";} }
3. 日志配置
在 A 服务和 B 服务的 logback.xml
中,配置日志输出格式,确保 traceId
能够被打印出来。
xml
复制
<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId}] %msg%n</pattern></encoder></appender><root level="info"><appender-ref ref="STDOUT" /></root> </configuration>
运行 HTML
测试流程
-
启动 A 服务和 B 服务。
-
访问 A 服务的
/call-b-service
接口。 -
查看 A 服务和 B 服务的日志输出,确保
traceId
一致。
A 服务日志示例:
复制
2023-10-01 12:00:00.000 [http-nio-8080-exec-1] INFO AServiceController - [123e4567-e89b-12d3-a456-426614174000] A Service - Calling B Service with traceId: 123e4567-e89b-12d3-a456-426614174000
B 服务日志示例:
复制
2023-10-01 12:00:00.100 [http-nio-8081-exec-1] INFO BServiceController - [123e4567-e89b-12d3-a456-426614174000] B Service - Processing request with traceId: 123e4567-e89b-12d3-a456-426614174000
总结
通过上述实现,我们完成了 A 服务调用 B 服务时的 traceId
传递和日志跟踪。traceId
通过 HTTP 头在服务间传递,并通过 MDC
在日志中输出,确保整个请求链路的日志能够被追踪。