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 服务中,使用 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() {
        // 获取当前请求的 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 在日志中输出,确保整个请求链路的日志能够被追踪。

相关推荐
陈年老古董1 小时前
Hive 函数学习笔记(下):窗口函数与经典案例
hive·笔记·学习
爱吃火鸡面呀1 小时前
DataX 实战:使用 HDFS Reader 读取 Hive ORC 表并输出到 Stream
hive·hadoop·hdfs
yumgpkpm2 小时前
CDP 7.3.1(Cloudera Runtime 7.3.1)VS Acceldata ODP 3.3.6.4 核心引擎详细版本对比
大数据·hive·hadoop·postgresql·zookeeper·spark·hbase
计算机毕业编程指导师3 小时前
【计算机大数据毕业设计选题】基于Hadoop+Spark的人工智能社交媒体情绪数据分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·人工智能·hadoop·计算机·毕业设计·课程设计·社交媒体
yumgpkpm3 小时前
(CDH 7)CDP Private Cloud Base 7.3.1 → Acceldata ODP 3.3.6.4 引擎迁移风险评估表
服务器·人工智能·hadoop·python·华为·zookeeper·hbase
计算机毕业编程指导师3 小时前
【计算机毕设】基于Hadoop的人口统计特征与肥胖风险关联分析的数据分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·数据分析·课程设计·肥胖风险
计算机毕业编程指导师3 小时前
【计算机毕业设计选题】基于Hadoop+Spark的乳腺癌数据分析与可视化系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
hadoop·python·spark·毕业设计·课程设计·乳腺癌·计算机毕设
yumgpkpm8 小时前
Acceldata ODP 3.3.6.4 vs CDP Private Cloud Base 7.3.2 对比
大数据·运维·服务器·hadoop·华为·zookeeper·hbase
yumgpkpm1 天前
Acceldata ODP(Open Data Platform)3.3.6.4(RHEL9)保姆级完整安装手册
大数据·人工智能·hive·hadoop·kafka·hbase·cloudera
sunxunyong1 天前
hs2偶发连接失败问题处理
hadoop