go向kafka发数据引发java系统消息反序列化失败毒丸现象

背景

公司项目采用微服务异构开发,java开发了一个消费模块,通过消费kafka进行业务处理。

  • java开发的业务系统,能被正常消费。
  • go开发的业务系统,发送消息正常,但是消费的时候一直报错。

排查问题

因为排查问题时没有截图,这里就简单描述下。

查资料发现,应该是反序列化失败触发了kafka的毒丸。 然后看spring-kafka的代码,找到了问题。在这里总结一下。

  1. 反序列化消息抛出异常
  2. 触发毒丸

补充一下,为什么java业务系统可以正常发消息。

因为消费模块dto信息拆分出来给业务模块使用,所以spring-kafka在发消息时会自动把这些信息写进去,并且不会出现dto类中字段不对应的问题。也就不会反序列化失败了。

序列化问题

消费模块是spring-boot开发的,用了spring-kafka,消息入口是这样的

@KafkaListener(topics = {"test"})
public void testListener(ConsumerRecord<String, TestDto> msg) {
    log.info("testListener 收到新的推送 : {}", msg);
    .....
}
复制代码

这里就发现了,spring-kafka会帮我们把消息内容转换为TestDto,但是它转换失败了。然后我跟着源码看了一下,发现它会找一个请求头。这就好办了, 把"__TypeId__":"xxxx.xxxx.TestDto" 这样的一个键值对放到请求头里。__TypeId__是key,xxxx.xxx.TestDao就是java代码中的那个类的包位置了。

然后看一下go的代码,发kafka消息用的是sarama库、


// 主要就是这块,在构建消息的时候,将请求头放进去。
message := sarama.ProducerMessage{
   Topic: "test",
   // marshal 是要发送的内容,是 marshal, err := json.Marshal(map[string]interface{}{"test":1})  
   Value: sarama.ByteEncoder(marshal),
   // __TypeId__ : yh.ci.message.dto.kafka.entity.message.AliSmsDto
   // 消费者反序列化时需要指定实体类。
   Headers: []sarama.RecordHeader{
      {
         Key:   []byte("__TypeId__"),
         Value: []byte("xxx.xxx.TestDto"),
      },
   },
}
//  NewProducer() 用来配置kafka地址啥的,就不展示了
client, err := NewProducer()

复制代码

大概就是这样,要让请求头的包和java代码中实际位置对应上。

毒丸

毒丸的配置我大概是参照这篇文章,感谢作者。 www.jdon.com/54521

简单列一下,

  1. 修改yml配置,
  2. 配置errorHandlerbean

yml

spring:
  kafka:
    consumer:
      bootstrap-servers: xxxx:9092
      # 是否自动提交offset
      enable-auto-commit: true
      # 当kafka中没有初始offset或offset超出范围时将自动重置offset
      # earliest:重置为分区中最小的offset;
      # latest:重置为分区中最新的offset(消费分区中新产生的数据);
      # none:只要有一个分区不存在已提交的offset,就抛出异常;
      auto-offset-reset: latest
      #毒丸配置
      key-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
      properties:
        # 默认的消费组ID
        group:
          id: defaultConsumerGroup
        "spring.json.trusted.packages": "*"
        spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer
        spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
    listener:
      missing-topics-fatal: false
复制代码

配置errorHandler

@Configuration
@EnableKafka
public class KafkaConfiguration {

    /**
     * 毒丸
     * https://www.jdon.com/54521
     *
     * Boot will autowire this into the container factory.
     */
    @Bean
    public LoggingErrorHandler errorHandler() {
        return new LoggingErrorHandler();
    }
}

复制代码

这样,如果在再遇到反序化之类的异常,会打印异常信息,然后跳过这条消息。当然你也可以考虑私信队列之类的方法。

おすすめ

転載: juejin.im/post/7039597151294324743