Kafka什么情况下会丢失消息?如何做到消息不丢失?

作为一个消息中间件,如果在运行过程中丢失消息,往往是很难排查和追溯的,Kafka也会存在丢失消息的情况,今天我想分析一下Kafka丢失消息的几个原因,以及如何尽可能的防止消息不丢失。在分析Kafka丢失消息的时候,我准备从broker、producer、consumer三端进行分析。

1.broker
producer发送消息到broker端,如果broker接收并保存该消息,返回ack标记到producer,那么认为该消息已经生产并发送完成。
kafka中ack有三种模式:
ack=0,即生产者发送完消息就认为该消息已经发送成功,并不会等到broker的响应,这种是效率比较高的一种方式,但极有可能丢失消息,所以一般不建议使用。
ack=1,即只要写入leader副本成功就认为消息发送成功,就会返回ack到producer,并不会等到该消息同步到其他follower才ack。一般情况下这种是没有问题的,但还是会存在丢失消息的情况,比如leader副本接收到了消息并返回ack到producer,但是此时ISR中的follower副本还没有同步完这条消息,此时leader副本挂了,ISR中的follower会被重新选举为leader,由于之前没有同步完该消息,导致该follower没有这条消息,之前producer也已经收到ack,认为已经发送成功,无法重发消息,那么这条消息就丢失了。(leader的HW是ISR中所有副本中LEO最靠前的那个位置,即消费者只能消费HW位置之前的消息),后面由于新成为leader的follower没有该消息,所以后面也消费不到了。
ack=-1/all,即不只leader副本要成功接收到消息,还要等到ISR中所有的follower都同步完该消息后,才返回ack给producer。这种方式是效率最差的,但可靠性是最高的。这样即使follower还没有同步完leader中的消息,leader挂掉了,由于没有返回ack给producer,那么producer会进行重试,除非一直重试都发送失败,超过了重试次数,这样肯定是服务器或系统出现了问题,不过这样我们可以通过其他方式将该条消息记录下来。如果follower已经同步完leader中的信息,leader挂了,那么follower中已经有了该消息,那么后面重选举变为leader后也会存在该消息,就不会丢失消息了。

通常和ack搭配使用的broker配置min.insync.replicas,即ISR中最小的副本数量,一般会配置>=2,如果ISR中的replica数量小于该配置的数量,则会抛出异常。如果不配置该参数的话,那么ISR中的follower的数量就可能为0,这样就变为了ack=1了。

2.Producer
producer也存在丢失消息的情况,为了提升效率,producer在发送数据时可以将多个请求缓存起来,然后批量发送。这些消息就会放在本地的buffer中,算是消息缓存区。producer可以按时间间隔将这些buffer数据发出。
在这里插入图片描述

但是buffer中的数据是危险的,他们保存在内存中,一旦producer停止了,或者producer客户端内存不够时,采取的策略时丢弃消息,那么这些消息也就丢失了。

在这里插入图片描述
这里有几个解决办法:
·将异步发送改为同步发送,或者在产生消息的时候,使用阻塞的线程池,并且线程数有一定上限。整体思路是控制消息产生速度。
·扩大buffer的容量,可以缓解这个情况的出现,但无法杜绝。
·消息不直接发送到buffer内存中,而且先将消息写道本地磁盘或者数据库中,再由另一个生产线程进行消息发送。

3.Consumer
kafka消费端提交offset的方式有自动提交和手动提交两种方式。
自动提交:enable.auto.commit=true,即消费者在调用poll方法时会拉取到要消费的数据,并且在指定的时间后将该消息进行提交,这个时间是可以配置的,如果我们在拿到消息后在指定的时间内没有操作完消息,甚至程序执行异常了,这时候消费者位移已经提交成功,而我们的程序没有处理成功,这样该消息就丢失了。
手动提交:enable.auto.commit=false,即由我们手动的控制位移。
同步提交:consumer.commitSync()
异步提交:consumer.commitAsync()
手动提交保证了消息至少会被消费一次,但是会造成重复消费的问题。
举例说明一种场景,假设你花钱从网上租了一本共有10章内容的电子书,该电子书的有效阅读时间是 1 天,过期后该电子书就无法打开,但如果在 1 天之内你完成阅读就退还租金。
为了加快阅读速度,你把书中的 10 个章节分别委托给你的 10 个朋友,请他们帮你阅读,并拜托他们告诉你主旨大意。当电子书临近过期时,这 10 个人告诉你说他们读完了自己所负责的那个章节的内容,于是你放心地把该书还了回去。不料,在这 10 个人向你描述主旨大意时,你突然发现有一个人对你撒了谎,他并没有看完他负责的那个章节。那么很显然,你无法知道那一章的内容了。
对于Kafka而言,就相当于consumer从kafka获取消息后开启了多个线程异步处理消息,而Consumer自动的向前更新位移,假如其中某个线程运行失败了,他负责的消息没有被处理成功,但是位移已经被更新了,那么这条消息对于consumer已经丢失了。
这是因为consumer自动提交位移,与你没有确认书籍内容被全部读完就将书归还类似,你没有真正地确认消息是否被消费就盲目的更新了位移。

Kafka无消息丢失配置
1.不要使用producer.send(msg),而要使用producer.send(msg, callback)。

2.设置acks=all。acks是producer的一个参数,代表了你对“已提交”消息的定义。如果设置成all,则表明所有副本broker都要接收到消息,该消息才算是“已提交”。

3.设置retries为一个较大的值。retries同样是producer的参数,对应前面提到的producer自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了retries > 0的producer能够自动重试消息发送,避免消息丢失。

4.设置unclean.leader.election.enable=false。这是broker端的参数,他控制的是哪些broker有资格竞选分区的leader。如果一个broker落后原先的leader太多,那么它一旦成为新的leader,必然会造成消息的丢失。故一般都要将该参数设置成false,即不允许这种情况发生。

5.设置replication.factor >= 3。这也是broker端的参数。就是将消息多保存几份,毕竟目前防止消息丢失的主要机制就是冗余。

6.设置min.insync.replicas > 1。这也是broker端的参数,控制的消息至少被写入多少副本才算“已提交”。设置成大于1可以提升消息持久性。在生产环境中不要使用默认值1。

7.确保replication.factor > min.insync.replicas。如果两者相等,那么只要有一个副本挂机,整个分区就无法正常工作了。我们不仅要改善消息的持久性,防止数据丢失,还要再不降低可用性的基础上完成。推荐设置成replication.factor = min.insync.replicas + 1。

8.确保消息消费完成再提交。Consumer端有个参数enable.auto.commit,最好把它设置成false,并采用手动提交位移的方式。就像前面说的,这对于单Consumer多线程处理的场景而言是至关重要的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值