👏大家好!我是和风coding,希望我的文章能给你带来帮助!
🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
📝点击 我的主页 还可以看到和风的其他内容噢,更多内容等你来探索!
📕欢迎参观我的个人网站:Gentlewind
文章目录
kafka 的架构
kafka 的架构非常简洁,主要是分布式架构,由 Producer、Broker、Consumer 组成。
🔊所以分析丢失的场景也会由这三部分来进行。
从 Kafka 整体架构图我们可以得出有三次消息传递的过程:
1)Producer 端发送消息给 Broker 端。
2)Broker 将消息进行同步并持久化数据。
3)Consumer 端从 Broker 将消息拉取并进行消费。
消息语义传递
首先我们先了解一下保证消息传递的一个通信模型:消息语义传递
他有三个类型:
- At Most Once(最多一次):消息在传递过程中最多被传递一次。也就是说消息可能会丢失,但不会重复传递
- At Least Once(至少一次):消息在传递中至少传递一次。也就是消息不会丢失,但可能重复传递
- Exactly Once(恰好一次):也就是精确的传递一次。既不丢失,也不重复传递
我们需要**根据具体的场景,选择需要的传递类型。**比如对消息丢失不能忍受,但是可以接收消息重复传递,就可以选择第二种类型。
然后再根据需求来对 kafka 进行相应的配置。就类似于 CAP 理论,这是一个取舍问题。
Producer
Producer 端发送消息到 Broker 端,消息丢失的情况可能会有:
- 网络波动导致消息没有发送到 Broker
- 发送途中 Broker 宕机了
- 消息太大超过了 Broker 的容量被拒收了
总结:丢失的原因就是消息根本没有发送到 broker 端
拓展:Producer 端为了提升发送效率,减少IO操作,发送数据的时候是将多个请求合并成一个个 RecordBatch ,并将其封装转换成 Request 请求「异步」将数据发送出去(也可以按时间间隔方式,达到时间间隔自动发送)
解决方案:
- ACK 机制
可以通过配置 ack 来对消息进行确认,进而保证消息不丢失。
具体 ack 的配置为:
acks = 0
:不进行确认,也就是实现第一种类型acks = 1
:消息发送到 broker 的分区后进行一次 ack 确认,确认接收成功后就完成了。但是它只保证了发送消息到主分区。如果从分区还没有同步完数据而主分区宕掉了,就会造成丢数据。acks = all
:对所有的主从分区进行 ack 确认,都成功接收消息才完成。这样的可靠性最高,但是相应的吞吐率也会更低。
其实就是同步的方式,Producer要保证消息到达服务器,就需要使用到消息确认机制,也就是说,必须要确保消息投递到服务端,并且得到投递成功的响应,确认服务器已接收,才会继续往下执行。
Broker
kafka 中,broker 接收到消息会先存放在 Pagecache (缓存)中,然后通过批量异步刷盘的策略来对数据进行同步和持久化。
Broker 将消息进行同步并持久化数据。消息丢失的情况有:
- 当刷盘之前 broker 宕掉了,然后推举出了一个落后数据的从分区。那么之前的数据就丢失了
解决方案:
- 同步刷盘:可以通过一些配置实现同步刷盘,可以保证消息不丢失,这样就算失败,也可以进行及时补偿。
当达到下面的消息数量时,会将数据flush到日志文件中。默认10000
#log.flush.interval.messages=10000
当达到下面的时间(ms)时,执行一次强制的flush操作。interval.ms和interval.messages无论哪个达到,都会flush。默认3000ms
#log.flush.interval.ms=1000
检查是否需要将日志flush的时间间隔
log.flush.scheduler.interval.ms = 3000
同样可以达到同步刷盘的效果。
- kafka 默认通过「**多 Partition (分区)多 Replica(副本)机制」**已经可以最大限度的保证数据不丢失,如果数据已经写入 PageCache 中但是还没来得及刷写到磁盘,此时如果所在 Broker 突然宕机挂掉或者停电,极端情况还是会造成数据丢失。
Consumer
Consumer 消费消息有两个步骤:
- 从 broker 中拉取消息
- 处理消息,然后标记消息已经消费并提交 offset 记录(消息位移记录),下次继续消费的时候,会接着上次的offset进行消费。
那么 Consumer 端从Broker 将消息拉取并进行消费,可能丢失的场景有:
-
开启了自动提交,如果开启了自动提交,那么系统会自动进行提交offset。可能会引起,并未消费掉,就提交了offset.引起数据的丢失。
-
并且自动提交也分两种情况:
-
-
拉取消息后「先提交 Offset,后处理消息」,如果此时处理消息的时候异常宕机,由于 Offset 已经提交了, 待 Consumer 重启后,会从之前已提交的 Offset 下一个位置重新开始消费, 之前未处理完成消息不会被再次处理,对于该 Consumer 来说消息就丢失了。
-
拉取消息后「先处理消息,在进行提交 Offset」, 如果此时在提交之前发生异常宕机,由于没有提交成功 Offset, 待下次 Consumer 重启后还会从上次的 Offset 重新拉取消息,不会出现消息丢失的情况, 但是会出现重复消费的情况,这里只能业务自己保证幂等性。
-
解决方案:
设置参数enable.auto.commit = false
, 采用手动提交位移的方式。这样如果消费失败的情况下,我们可以不断地进行重试。所以,消费端不要设置自动提交,一定设置为手动提交才能保证消息不丢失。而幂等的问题可以在业务逻辑中进行判断。