at most once:最多一次。消息可能丢失也可能被处理,但最多只会处理一次
at least once:至少一次。消息不会丢失,但是可能被处理多次
exactly once:精准传递一次。消息被处理且只会被处理一次
理想情况下肯定是希望消息传递是严格exactly once。但是很难做到
kafka有三次消息传递的过程
1.生产者发消息给kafka broker
2.kafka broker消息同步和持久化
3.kafka broker将消息传递给消费者
以上三步都可能会丢失消息导致整个kafka丢失消息,那应该如何最大程度上避免消息丢失的问题呢。
先介绍下生产者发送消息的一般过程(部分流程与具体配置项强相关,这里先忽略)
1.生产者是与leader直接交互的,所以先从集群获取topic对应分区的leader元数据
2.获取到leader分区元数据后直接将消息发送过去
3.kafka broker 对应的leader分区收到消息后写入文件持久化
4.follower拉去leader消息与leader的数据保持一致
5.follower消息拉去完毕后会给leader回复ack确认消息
6.leader和follower分区同步完,leader分区会给生产者回复ack确认消息
Kafka通过配置request.required.acks属性来确认消息的生产:
0:表示不进行消息接收是否成功的确认,无法保证消息是否发送成功,一般生产不会使用
1:表示当leader接受成功时确认,只要leader存活就可以保证不丢失,同时保证了吞吐量,但是如果leader挂了就会导致消息丢失
-1/all:表示leader和follower都接受成功时确认,可以最大限度保证消息不丢失,但是吞吐量低,也会有消息重复消费的问题
操作系统本身有一层缓存,叫做Page cache。当往磁盘文件写入数据时,系统会先将数据流写进缓存中,至于什么时候将缓存写入文件是由操作系统自行决定。
Kafka提供了一个参数来控制是不是主动flush,如果kafka写入到缓存中就立即flush然后再返回Producer叫同步;写入缓存后立即返回Producer不调用flush叫异步。
所以如果采用异步机制的话在服务器宕机或者断电会导致消息丢失,当然这种情况非常极端
消费者通过pull模式主动的去kafka集群拉取消息,与生产者相同的是消费者消费消息同样与leader分区交互。
多个消费者可以组成一个消费者组,每个消费者组都有一个组Id。同一个消费者组的消费者可以消费同一topic下不同分区的数据,但是不会出现多个消费者消费同一分区的数据。
消费者消费的进度通过offset保存在kafka集群_consumer_offsets这个topic中。
消息消费的时候主要分为两个阶段,当然这两个阶段谁先谁后是可以配置的
1.表示消息已被消费,并 commit offset坐标
2.处理消息
如果先commit在处理消息,如果在处理消息时候异常了,但是此时offset已被提交,所以会导致消息永远不会被消费,也就出现了消息丢失
如果先处理消息再commit offset,会导致消息被重复消息的场景,需要下游做好幂等。
总结kafka在以上三个阶段都会丢失消息,所以在生产中实现exactly once是非常困难的事情,业界最佳实现是业务侧做好补偿机制以及幂等原则,哪怕出现消息丢失也有兜底方案。



