ELK traceId 通过A服务调用B服务举例

在分布式系统中,traceId 需要在服务之间传递,以确保整个请求链路的日志能够被追踪。下面我们通过一个具体的例子,展示如何通过 A 服务调用 B 服务时传递 traceId,并确保日志中能够正确记录 traceId


实现思路

  1. A 服务生成 traceId :在 A 服务的 HandlerInterceptor 中生成 traceId,并将其放入 MDC

  2. A 服务调用 B 服务时传递 traceId :在 A 服务调用 B 服务时,将 traceId 通过 HTTP 头传递给 B 服务。

  3. B 服务接收 traceId :在 B 服务中,通过 HandlerInterceptor 或过滤器从 HTTP 头中提取 traceId,并将其放入 MDC

  4. 日志输出 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";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 生成唯一的 traceId
        String traceId = UUID.randomUUID().toString();
        // 将 traceId 放入 MDC
        MDC.put(TRACE_ID, traceId);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 请求完成后清理 MDC 中的 traceId
        MDC.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 {

    @Autowired
    private TraceIdInterceptor traceIdInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(traceIdInterceptor);
    }
}
(3) 调用 B 服务时传递 traceId

在 A 服务中,使用 RestTemplateFeign 调用 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() {
        // 获取当前请求的 traceId
        String traceId = MDC.get("traceId");
        logger.info("A Service - Calling B Service with traceId: {}", traceId);

        // 设置 HTTP 头,传递 traceId
        HttpHeaders 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";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        // 从 HTTP 头中获取 traceId
        String traceId = httpRequest.getHeader("X-Trace-Id");
        if (traceId == null || traceId.isEmpty()) {
            // 如果头中没有 traceId,生成一个新的
            traceId = UUID.randomUUID().toString();
        }

        // 将 traceId 放入 MDC
        MDC.put(TRACE_ID, traceId);

        try {
            chain.doFilter(request, response);
        } finally {
            // 请求完成后清理 MDC 中的 traceId
            MDC.remove(TRACE_ID);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

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

    @Bean
    public 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() {
        // 获取当前请求的 traceId
        String 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


测试流程

  1. 启动 A 服务和 B 服务。

  2. 访问 A 服务的 /call-b-service 接口。

  3. 查看 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 在日志中输出,确保整个请求链路的日志能够被追踪。

相关推荐
Gl�ria1 分钟前
Hadoop MapReduce / Hive 场景:ReduceTask 热点 Key 排查
运维·hadoop·数据倾斜
Q264336502320 小时前
【有源码】基于机器学习的电信网络诈骗话术语义识别与可视化分析系统 XGBoost算法+Hadoop+Spark
大数据·hadoop·算法·机器学习·spark·毕业设计·课程设计
Q264336502321 小时前
【有源码】基于 Hadoop 的强化学习智能体轨迹数据挖掘与可视化分析系统大数据毕设项目
大数据·hadoop·机器学习·数据挖掘·spark·课程设计·数据可视化
Q26433650231 天前
【有源码】基于大数据的药品多维度属性分析可视化系统 基于Spark的药品属性聚类与关联规则可视化分析系统
hadoop·机器学习·数据挖掘·spark·毕业设计·课程设计·大数据项目
网络豆1 天前
Apache Hive 鸿蒙 PC 适配全记录:以 Qt 客户端打通 HiveQL、监控与元数据访问
hive·apache·harmonyos
Q26433650231 天前
【有源码】基于大数据的零售交易者行为特征与生存状况数据分析及可视化 Hadoop+Spark大数据项目
大数据·hadoop·信息可视化·数据挖掘·数据分析·spark·毕业设计
Q26433650231 天前
【有源码】基于 Hadoop 生态的化妆品销售数据存储分析与可视化 面向化妆品行业的用户画像构建与销售机会识别研究
大数据·hadoop·python·机器学习·spark·毕业设计·课程设计
计算机编程-吉哥2 天前
基于机器学习的城市交通拥堵分析与预测平台【计算机毕业设计选题·机器学习·随机森林算法】
hadoop·算法·随机森林·机器学习·课程设计·计算机毕业设计选题·大数据毕业设计选题推荐
fastjson_2 天前
Hive3.1.3 安装使用
hive
计算机源码社2 天前
基于Hadoop+Spark的黄金市场历史数据特征分析与可视化大屏 基于K-Means聚类算法的黄金历史价格阶段划分研究
大数据·hadoop·python·数据挖掘·数据分析·spark·毕业设计