【七】kafka可靠性、有序性、重复消费、丢数据、线程安全

一、哪些情况会造成重复消费

1.生产者挂了重启

场景:

生产者设置的ACK是ALL,及需要所有副本都保存了消息才叫发送成功。

假设生产者发送一条信息,leader已经收到且已经保存了,follower还没来得及同步,leader就挂了。

此时会被认为是发送失败,重发。

而刚好重发的时候旧的leader又重启复活了,那旧的leader会保存两次同样的数据。

不好意思,我可能写错了。这个场景不会导致消息重复。

因为:

为了实现 Producer 的幂等语义,Kafka 引入了Producer ID(即PID)和Sequence Number。每个新的 Producer 在初始化的时候会被分配一个唯一的 PID,该 PID 对用户完全透明而不会暴露给用户。

对于每个 PID,该 Producer 发送数据的每个<Topic, Partition>都对应一个从 0 开始单调递增的Sequence Number

类似地,Broker 端也会为每个<PID, Topic, Partition>维护一个序号,并且每次 Commit 一条消息时将其对应序号递增。对于接收的每条消息,如果其序号比 Broker 维护的序号(即最后一次 Commit 的消息的序号)大一,则 Broker 会接受它,否则将其丢弃:

  • 如果消息序号比 Broker 维护的序号大一以上,说明中间有数据尚未写入,也即乱序,此时 Broker 拒绝该消息,Producer 抛出InvalidSequenceNumber
  • 如果消息序号小于等于 Broker 维护的序号,说明该消息已被保存,即为重复消息,Broker 直接丢弃该消息,Producer 抛出DuplicateSequenceNumber

上述设计解决了 0.11.0.0 之前版本中的两个问题:

  • Broker 保存消息后,发送 ACK 前宕机,Producer 认为消息未发送成功并重试,造成数据重复
  • 前一条消息发送失败,后一条消息发送成功,前一条消息重试后成功,造成数据乱序

2.消费者自动提交偏移量

场景

消费者A,第一次poll了100条,刚好第一次提交偏移量也是100+1(5秒提交一次),在后面的3秒中,消费者A又poll了2次,每次100条,相当于此时消费者A已经消费到了偏移量300了,此时才过3秒,还没有到下一次触发自动提交的时间。这是消费者A挂了,发生了消费再平衡,由B来接着消费这个分区,那B就是从101偏移量开始消费,那么101-300都被重复消费了。

3.消费者手动提交偏移量

场景:

如果先拉取消息,消费完后再更新offset位置。然而还没消费完,就报错或者再均衡,那么该次拉取的偏移量就没有提交,就导致会重复消费。

二、哪些情况会造成漏消费

1.生产者没有ACK

场景:

生产者fire-and-forget(发后即忘),无论有几个副本保存了消息,都认为发送成功,那么有可能跟本没副本保存消息

2.生产者ACK=1

场景:

生产者发送消息后,只要有一个副本即leader保存了消息就认为发送成功,那么leader挂了,就会被漏消费

3.生产者ACK=ALL 但是partition副本数量为1

场景:

生产者发送消息后,虽然要等到所有副本都保存了才认为发送成功,但是这里的“所有副本”就只有1个即leader,那么leader挂了就会被漏消费

3.消费者自动提交偏移量

场景:

消费者A,第一次poll了100条,刚好第一次提交偏移量也是100+1(5秒提交一次),但是拉取的这100条才处理了50条,A就挂了,相当于有50条已经拉取了的,已经提交了偏移量了的,还没处理。发生了消费再平衡,由B来接着消费这个分区,B从101开始消费,那么相当于51-101的数据丢失了

4.消费者手动提交偏移量

场景:

先提交位移,但是消息还没消费完就宕机了,造成了消息没有被消费。

三、如何保证可靠性

我理解的可靠性就是保证消息必然送达

1.生产者

ACK=ALL

retries=N 发送失败一直重试

异常时写日志

2.kafka集群

 replication.factor > 1,每个partition至少有2个副本

 min.insync.replicas > 1,每个leader至少和2个follower保持联系

3.消费者

 手动提交偏移量

 消费后才提交偏移量

四、如何保证有序性

1.生产者

发送消息的分区策略,根据Key来指定分区。

有顺序要求的消息,key要一样,即考虑使用业务唯一ID,这样就会被发送到相同的分区中

2.kafka集群

同一个分区中的数据天然有序,分区相当于是个双端队列

3.消费者

同一个主题下的分区,只能被同一个消费者组中的一个消费者消费。

五、线程安全

1.线程封闭,即为每个线程实例化一个 KafkaConsumer 对象

一个线程对应一个 KafkaConsumer 实例,我们可以称之为消费线程。一个消费线程可以消费一个或多个分区中的消息,所有的消费线程都隶属于同一个消费组。

消费逻辑在该消费线程中。

2.消费者程序使用单或多线程获取消息,同时创建多个worker线程执行消息处理逻辑。
获取消息的线程可以是一个,也可以是多个,每个线程维护专属的 KafkaConsumer 实例,处理消息则交由worker线程池来做,从而实现消息获取与消息处理的真正解耦。

两种方式对比

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值