Kafka如何配置生产者拦截器和消费者拦截器

Kafka 的生产者拦截器和消费者拦截器允许你在消息发送前后以及消息消费前后嵌入自定义逻辑,用于实现监控、审计、消息修改等功能。本文我们就用一个最常见的传递TraceId的案例来说明下这两类拦截器如何来使用。

生产者发送拦截器

生产者拦截器需要实现 org.apache.kafka.clients.producer.ProducerInterceptor 接口。在这个拦截器中,我们把保存到ThreadLocal中的traceId设置到消息的header中。

步骤 1:实现拦截器类

创建一个类,实现 ProducerInterceptor 接口。该接口有两个核心方法:

  • onSend(ProducerRecord record): 在消息被序列化和计算分区之前调用。你可以修改或记录消息。

  • onAcknowledgement(RecordMetadata metadata, Exception exception): 在消息被服务器确认(成功或失败)之后调用。这会在生产者回调触发之前调用。注意:该方法不要在 ProducerInterceptor 中实现耗时逻辑,因为它会阻塞生产者。

java 复制代码
public class SendTraceIdInterceptor implements ProducerInterceptor<String, String> {
    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> producerRecord) {
        // 把TheadLocal中traceId设置到header中
        producerRecord.headers().add(RequestContext.TRACE_ID, RequestContext.getTraceId().getBytes());
        return producerRecord;
    }

    @Override
    public void onAcknowledgement(RecordMetadata recordMetadata, Exception e) {
        if(e == null){
            log.info("send successfully");
        } else {
            log.error("send error : {}", e);
        }
    }

    @Override
    public void close() {
    }
    // 这里可以拿到所有的producer的配置信息
    @Override
    public void configure(Map<String, ?> map) {
        log.info("configure:{}", map);
    }
}
步骤 2:在生产者配置中指定拦截器
yml 复制代码
spring:
  kafka:
    bootstrap-servers: localhost:9092  # Kafka服务器地址
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      properties:
        interceptor.classes: com.github.xjs.kafka.interceptor.SendTraceIdInterceptor

消费者接收拦截器

消费者拦截器需要实现 org.apache.kafka.clients.consumer.ConsumerInterceptor 接口。在这个拦截器中我们读取消息中的header并重新设置到ThreadLocal中。

步骤 1:实现拦截器类

创建一个类,实现 ConsumerInterceptor 接口。该接口也有两个核心方法:

  • onConsume(ConsumerRecords records): 在消息被反序列化之后、传递给消费者poll()方法返回之前调用。你可以修改或过滤消息。
  • onCommit(Map offsets): 在消费者提交偏移量之后调用。
java 复制代码
public class ReceiveTraceIdInterceptor implements ConsumerInterceptor<String, String> {

    private static Logger log = LoggerFactory.getLogger(ReceiveTraceIdInterceptor.class);

    @Override
    public ConsumerRecords<String, String> onConsume(ConsumerRecords<String, String> records) {
        for(Iterator<ConsumerRecord<String, String>> recordIterator = records.iterator(); recordIterator.hasNext();){
            ConsumerRecord<String, String> record = recordIterator.next();
            Headers headers = record.headers();
            if(headers == null){
                continue;
            }
            for(Iterator<Header> headerIterator = headers.iterator(); headerIterator.hasNext();){
                Header header = headerIterator.next();
                // 从header中获取traceId, 并保存到ThreadLocal          
                if(Objects.equals(header.key(), RequestContext.TRACE_ID)){
                    RequestContext.setTraceId(new String(header.value()));
                }
            }
        }
        return records;
    }
    @Override
    public void onCommit(Map<TopicPartition, OffsetAndMetadata> offsets) {
    }
    @Override
    public void close() {
    }
    // 这里可以拿到所有的消费者的配置
    @Override
    public void configure(Map<String, ?> configs) {
        log.info("consumer configure:{}", configs);
    }
}
步骤 2:在消费者配置中指定拦截器
xml 复制代码
spring:
  kafka:
    bootstrap-servers: localhost:9092  # Kafka服务器地址
    consumer:
      group-id: my-group  # 默认的消费者组ID
      auto-offset-reset: earliest  # 如果没有初始偏移量或偏移量已失效,从最早的消息开始读取
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      properties:
        interceptor.classes: com.github.xjs.kafka.interceptor.ReceiveTraceIdInterceptor    

总结

位置
  • 生产者拦截器:在消息序列化和分区之前(onSend)以及确认之后(onAcknowledgement)调用。
  • 消费者拦截器:在消息反序列化之后、返回给用户之前(onConsume)以及提交偏移量之后(onCommit)调用。
配置

使用 ProducerConfig.INTERCEPTOR_CLASSES_CONFIGConsumerConfig.INTERCEPTOR_CLASSES_CONFIG 属性进行配置。

值是该拦截器类的全限定名,多个拦截器用逗号分隔,它们会按照配置的顺序执行。

用途
  • 监控和审计:记录消息发送/接收的成功失败、延迟等。
  • 消息修改:在发送前给消息添加统一前缀或头信息。
  • 自定义指标:与监控系统(如 Prometheus)集成,收集特定指标。
  • 过滤:消费者端可以尝试过滤消息,比如:本地local开发环境和测试服务器的test环境可能使用的是同一套kafka服务,我们可以在消息头中传递环境标识,在消费者端去过滤只属于自己这个环境的消息,从而防止引起混乱。
相关推荐
NE_STOP13 小时前
MyBatis-mybatis入门与增删改查
java
孟陬16 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端
想用offer打牌16 小时前
一站式了解四种限流算法
java·后端·go
华仔啊17 小时前
Java 开发千万别给布尔变量加 is 前缀!很容易背锅
java
也些宝18 小时前
Java单例模式:饿汉、懒汉、DCL三种实现及最佳实践
java
Nyarlathotep011318 小时前
SpringBoot Starter的用法以及原理
java·spring boot
wuwen518 小时前
WebFlux + Lettuce Reactive 中 SkyWalking 链路上下文丢失的修复实践
java
SimonKing19 小时前
GitHub 10万星的OpenCode,正在悄悄改变我们的工作流
java·后端·程序员
Seven9719 小时前
虚拟线程深度解析:轻量并发编程的未来趋势
java