前言
遇到很多kafka相关问题,一看就会,一问就跪,在这里简单总结,不作深入探讨。
提示:以下是本篇文章正文内容,下面案例可供参考
一、kafka会不会丢失消息?
会!
kafka发送消息一般分三个步骤:
1、生产者发送消息
ack=1(默认),数据发送到kafka,经过leader成功接收消息的确认就算成功(吞吐量高)。
ack=0,生产者发送后不等待任何返回。
ack=-1,需要ISR(副本同步队列)中所有follewer都确认接收到数据才算一次发送。当ISR中所有Replica都向leader发送ack时,leader才commit(不会丢失消息,吞吐量较低)。
2、kafka broker存储数据
kafka为了得到更高的性能和吞吐量,将数据异步批量存储在磁盘,刷盘过程中,为提高性能,减少刷盘次数,采用批量刷盘的做法。
若在刷盘过程中,副本未完成同步时系统出现故障,会造成数据丢失。
3、消费者消费数据
自动提交offset,一定时间间隔将收到的消息进行commit,commit过程和消费消息的过程是异步的。如果消费过程未成功,但commit提交了,消息会丢失。
手动提交offset可保证“至少消费一次”,但随之可能出现重复消费情况。
消息丢失场景总结:
1、网络异常
2、客户端异常
3、缓冲区满(增加消费者,提高吞吐量)
4、副本异常
二、kafka如何解决重复消费问题
消费信息用唯一标识标记消费情况,一般由consumergroup+topic+partition+offset组成。
我在项目中的完成过程如下:
1、获取分布式锁
2、通过redis检查是否已经消费
3、消费
4、更新消费状态
5、释放锁
代码如下:
@Slf4j
public class AlarmMessagePushTask extends BaseWorkTask {
/**
* 计数器闭锁
**/
private CountDownLatch countDownLatch;
/**
* kafka 消费偏移量
**/
private Acknowledgment ack;
/**
* 报警信息
**/
private Alarm alarm;
/**
* 告警信息
**/
private AlarmDetail alarmDetail;
/**
* 设备坐标信息
*/
private AlarmDeviceCoordinate alarmDeviceCoordinate;
/**
* 报警规则配置信息
**/
private RuleConfiguration ruleConfiguration;
/**
* 报警配置规则
*/
private MessagePushConfigurationDto messagePushConfiguration;
/**
* 失败重试次数,默认为3次
**/
private Integer retriesCount = 3;
/**
* redis 操作模板
**/
private StringRedisTemplate stringRedisTemplate;
/**
* 报警信息操作的 service
**/
private AlarmService alarmService;
/**
* 告警信息操作的 service
**/
private AlarmDetailService alarmDetailService;
/**
* 字典信息操作的 service
**/
private DicService dicService;
/**
* 处理告警设备坐标的 service
*/
private AlarmDeviceCoordinateService alarmDeviceCoordinateService;
/**
* @param countDownLatch 计数器闭锁
* @param ack kafka 消费偏移量
* @param ruleConfiguration 报警规则配置信息
* @param messagePushConfiguration 统一消息推送配置信息
*/
public AlarmMessagePushTask(@Validated(value = {ValidationGroup_Add.class}) MessagePushConfigurationDto messagePushConfiguration,
@Validated(value = {ValidationGroup_Add.class}) RuleConfiguration ruleConfiguration,
CountDownLatch countDownLatch, Acknowledgment ack) {
this.countDownLatch = countDownLatch;
this.ack = ack;
this.ruleConfiguration = ruleConfiguration;
this.messagePushConfiguration = messagePushConfiguration;
this.alarmService = SpringBeanUtil.getBean(AlarmService.class);
this.stringRedisTemplate = SpringBeanUtil.getBean(StringRedisTemplate.class);
this.alarmDetailService = SpringBeanUtil.getBean(AlarmDetailService.class);
this.dicService = SpringBeanUtil.getBean(DicService.class);
this.alarmDeviceCoordinateService = SpringBeanUtil.getBean(AlarmDeviceCoordinateService.class);
}
/**
* 工作任务线程
*/
@Override
public void run() {
try {
Assert.notNull(alarmService, "alarmService is not null");
Assert.notNull(ruleConfiguration, "ruleConfiguration is not null");
Assert.notNull(alarmDetailService, "alarmDetailService is not null");
Assert.notNull(stringRedisTemp