kafka组件traceId增强

问题背景

kafka 组件在排查生产者和消费者问题的时候,经常出现日志不匹配的情况,为了解决这个问题,本文实现 traceId 在kafka 组件的生产者和消费者之间传递,达到日志匹配,快速排查问题目的。

实现方案

1、新增生产者拦截器,读取 MDC 中的 traceId 并放入 kafka 消息的 headers 中;

2、新增消费者拦截器,读取 kafka 消息 headers 中的 traceId,覆写 MDC 的 traceId。

新增两个拦截器的方案对代码几乎没有侵入性,很优雅的解决了我们的问题。

生产者拦截器

1、kafka 消息发送前,进入到生产者拦截器,如果 MDC.get("trace_id") 存在值,则读取当前 MDC 中的 traceId 并放入消息的 headers 中;

2、如果 MDC.get("trace_id") 不存在,则生成一个 traceId,并写入消息的 headers 中。

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.ProducerInterceptor;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.kafka.common.header.Header;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@Component
public class KafkaTraceIdProducerInterceptor implements ProducerInterceptor<Integer, String> {

    public static final String TRACE_ID = "trace_id";
    @Override
    public ProducerRecord<Integer, String> onSend(ProducerRecord<Integer, String> producerRecord) {
        producerRecord.headers()
                .add(new Header() {
                    @Override
                    public String key() {
                        return TRACE_ID;
                    }

                    @Override
                    public byte[] value() {
                        return getOrGenerateTraceId()
                                .getBytes(StandardCharsets.UTF_8);
                    }
                });
        return producerRecord;
    }

    @Override
    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
    }

    @Override
    public void close() {
    }

    @Override
    public void configure(Map<String, ?> configs) {
    }

    /**
     * 获取当前请求TraceId或生成新的traceId
     *
     * @return traceId
     */
    public static String getOrGenerateTraceId() {
        return Optional.ofNullable(MDC.get(TRACE_ID))
                .orElseGet(UUID.randomUUID()::toString);
    }
}

消费者拦截器

读取消息 headers 中的 traceId,覆写 MDC 的 traceId,消息消费完成后清理相关的 MDC。

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.header.Header;
import org.slf4j.MDC;
import org.springframework.kafka.listener.RecordInterceptor;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

@Slf4j
@Component
public class KafkaConsumerInterceptor<K, V> implements RecordInterceptor<K, V> {

    private final static String TRACE_ID = "trace_id";


    @Override
    public ConsumerRecord<K, V> intercept(ConsumerRecord<K, V> consumerRecord) {
        return consumerRecord;
    }

    @Override
    public ConsumerRecord<K, V> intercept(ConsumerRecord<K, V> record, Consumer<K, V> consumer) {
        try {
            Header traceHeader = record.headers().lastHeader(TRACE_ID);
            String traceId = (traceHeader != null && traceHeader.value() != null)
                    ? new String(traceHeader.value(), StandardCharsets.UTF_8)
                    : UUID.randomUUID().toString();
            MDC.put(TRACE_ID, traceId);
        } catch (Exception e) {
            log.error("处理Kafka消息头异常", e);
        }
        return record;
    }

    @Override
    public void success(ConsumerRecord<K, V> record, Consumer<K, V> consumer) {
        MDC.clear();
    }

    @Override
    public void failure(ConsumerRecord<K, V> record, Exception exception, Consumer<K, V> consumer) {
        MDC.clear();
    }

}

问题延伸

如果遇到需要打印生产者和消费者接口日志或者遇到安全问题,生产者消息加密,消费者消息解密,都可以通过上述增加生产者拦截器和消费者拦截器来解决问题。

相关推荐
学传打活9 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活15 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16822 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心25 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u0103055271 个月前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055271 个月前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝1 个月前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055271 个月前
Java图像处理实战指南
人工智能·1024程序员节
u0103055271 个月前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节