十家面试,有八家会问的问题:“kafka会不会丢消息呢”。
有些情况下消息可能会消失。这可能是由于配置错误或误解Kafka的内部原理所致。本文将解释数据丢失可能发生的情况
Provider(Publisher):确认的时候
当消息被发送到发布者时,发布者等待来自代理的确认(ACK)。有三个可用的配置选项:
-
acks = all
- 当所有副本都确认已保存消息时,代理才会返回ACK。 -
acks = 1
- 当领导副本保存消息时返回ACK,但不会等待副本执行相同的操作。 -
acks = 0
- 生产者不会等待来自领导副本的确认。
由于最后一个选项显而易见的是:发送并忘记,第二个选项可能会导致数据丢失。在某种情况下,生产者将收到消息已保存的确认,但leader副本在确认之后宕机了并且无法启动。当新的副本被选举时没有该消息,所以消息永远消失了。
在JVM中,默认情况下ack
设置为1
,而在golang中为all
。正如您所看到的,不同语言实现存在差异,因此最好显式设置此值。
Provider(Publisher):缓冲区
为了提高性能(或减少网络使用),机器会启用缓冲。当调用发布方法时,消息不会立即发送,而是在缓冲区达到最大容量或给定的时间间隔内发送。 这些行为由batch.size
(以字节为单位)和linger.ms
参数控制。如果达到其中任何一个限制,消息将立即发送。所以,这需要特别强调:客户端将收到消息已发送的信息,但这并不是真的。所以,如果应用程序在刷新缓冲区之前崩溃,则数据将无法恢复。所以消息永远消失了。
请注意,这些参数可能因语言实现不同而不同。在JVM中,batch.size
是缓冲区中的字节数(默认为16384
字节),但在kafka-go
中,该参数描述了缓冲区中的消息数(默认为100
)。更重要的是,JVM用户默认将linger.ms
设置为0
,但kafka-go
用户将其设置为1
秒。
在JVM实现中,尽管设置linger.ms = 0
,消息仍然可以一起发送。这发生在负载较重的情况下 - 时间相近的消息将被批处理。
Consumer(Subscriber):Offsets
在消费消息时,消费者(订阅者)将其当前偏移量发送到代理。这是数据丢失可能发生的地方。
想象一种情况,一个消费者接收了两条消息:A 和 B。所有消息都在并行处理。处理消息时,B 成功了,偏移量被提交。然而,在处理消息 A 时,出现了一个错误。因为消息 B 的偏移量更大,Kafka 将保存最新的偏移量,消息 A 将永远不会返回给消费者。所以消息永远消失了。
Broker:已提交并不意味着已保存到磁盘
Kafka 在 Linux 系统上将消息保存到文件系统缓存(filesystem cache
)中,但不会等待消息持久化到硬盘上。 这意味着如果只有一个副本或 acks=1
,即使Broker返回了 ACK,Broker也可能崩溃并丢失消息。所以消息永远消失了。
Broker:已保存到硬盘并不意味着它不会消失
leader 与 follower 在数据同步上是有时间差的。 例如,当一个 follower 代理落后于 leader,但仍被认为是同步的(滞后时间由 replica.lag.time.max.ms
参数配置,默认为 500),然后 leader 崩溃。新的 leader 被选举出来,但它没有收到消息。所以消息永远消失了。
总结
Kafka 是一个具有高容量、一致性和延迟的优秀工具。另一方面,很多这些东西取决于生产者、消费者以及broker的配置。
本文列举了几点消息可能丢失的场景。所以,面试遇到这个问题,抛出去就好了
附
本文是我翻译的一篇英文:When you can lose messages in Kafka
初始我用了 ChatGPT翻译了一遍,算是感受了下 ChatGPT 的魅力。准确率80%吧。但别误会,我又人工检验和修改了一遍。
然后我又问了 ChatGPT 一个问题,它也给了我答案。如下图