我们的业务中使用到了kafka来实现一些功能,但是最近发现经常出现消息丢失的情况,我们用的org.springframework.kafka,配置如下:
spring:
kafka:
producer:
bootstrap-servers: xxx:9092
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
client-id: xxx
acks: 1
由于我们没有对KafkaTemplate发送端进行相应的配置,所以发送消息失败也很难找到错误信息.Spring 提供了一个producerListener接口,在发送成功/失败时会调用这个接口的onSuccess和onError方法,我们只需要实现这个接口就可以在发送成功/失败的时候处理自己的业务逻辑。于是我们先加上了如下的kafka配置:
@Slf4j
@Configuration
public class KafkaConfig {
@Bean
public KafkaTemplate<String, String> kafkaTemplate(ProducerFactory<String, String> producerFactory) {
KafkaTemplate<String, String> kafkaTemplate = new KafkaTemplate<>(producerFactory);
kafkaTemplate.setProducerListener(kafkaProducerListener());
return kafkaTemplate;
}
@Bean
public ConsumerAwareListenerErrorHandler consumerAwareListenerErrorHandler() {
return (message, e, consumer) -> {
log.error("consumer aware listener error is {}", e.getMessage());
return null;
};
}
@Bean
public KafkaProducerListener kafkaProducerListener() {
return new KafkaProducerListener();
}
static class KafkaProducerListener implements ProducerListener<String, String> {
@Override
public void onError(ProducerRecord producerRecord, Exception exception) {
log.error("kafka producer error, error mess is {}", exception.getMessage());
}
@Override
public void onError(String topic, Integer partition, String key, String value, Exception exception) {
log.error("kafka producer error, topic is {}, partition is {}, key is {}, value is {}, error mess is {}",
topic, partition, key, value, exception.getMessage());
}
}
}
这样,KafkaTemplate在发送消息失败就会进入OnError方法,打印出错误信息。
运行一段时间后,在elk上找到了如下的日志:
查找资料发现,这是由于kafka客户端broker reBalence 选举造成的:producer在向kafka broker写的时候,刚好发生选举,本来是向broker0上写的,选举之后broker1成为leader,所以无法写成功,就抛异常了。网上给的建议是修改retries参数,设为3,默认为0。
参考资料:
https://docs.confluent.io/current/installation/configuration/producer-configs.html#retries
https://blog.csdn.net/lbh199466/article/details/102968695