一、ACK机制
为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 的每个 partition 收到 producer 发
送的数据后,都需要向 producer 发送 ack(acknowledgement 确认收到),如果 producer 收到
ack,就会进行下一轮的发送,否则重新发送数据。
二、副本同步策略
方案一:半数以上完成同步,就发送ACK
优点:低延迟
缺点:选举新的leader时,容忍N台节点的故障,需要2N+1台副本
方案二:全部完成同步,才发送ACK
优点:选举新的leader时,容忍N台节点的故障,需要N+1台副本
缺点:延迟高
Kafka 选择了第二种方案,原因如下:
1. 同样为了容忍 n 台节点的故障,第一种方案需要 2n+1 个副本,而第二种方案只需要 n+1个副本,
第一种方案会造成大量数据的冗余。
2.虽然第二种方案的网络延迟会比较高,但网络延迟对kafka的影响较小
三、ISR机制
采用第二种方案之后,设想以下情景:leader 收到数据,所有 follower 都开始同步数据, 假如有一个follower因为某种故障,迟迟不能与 leader 进行同步,那 leader就要一直等下去,直到它完成同步才能发送 ack。如何破解这个问题呢?
Leader 维护了一个动态的 in-sync replica set (ISR),意为和 leader 保持同步的 follower 集合。当 ISR中的 follower 完成数据的同步之后,leader 就会给客户端发送 ack。如果 follower长时间未向leader同步数据,则该follower将被踢出ISR,该时间 阈值由replica.lag.time.max.ms参数设定。Leader 发生故障之后,就会从 ISR 中选举新的 leader。
四、可靠级别
并不是所有场景都需要数据完全可靠,例如某些日志都是一些数据是可以容忍少量数据丢失的,所以没
有必要等待ISR中的 follower 全部接收成功在发送ACK。
因此Kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,自行选择级别。
acks:
0:producer不等待broker的ack,提供了最低延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时可能丢失数据
1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么就会丢失数据
-1 or all:producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack,但是如果在follower同步成功后,broker发送ack之前,leader发生故障,那么会造成数据重复
五、故障处理
1、follower故障
follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会读取本地磁盘记录的上次HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步。等待follower的LED大于等于该partition的HW,即Follower追上leader之后,就可以重新加入ISR
2、leader故障
leader发生故障之后,会从ISR中选出一个新的leader,之后为保证多副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。注意:这只能保证副本的之间的数据一致性,并不能保证数据不丢失和不重复
六、Exactly Once语义
将服务器的 ACK 级别设置为-1,可以保证 Producer 到 Server 之间不会丢失数据,即 At Least Once语义。相对的,将服务器 ACK 级别设置为 0,可以保证生产者每条消息只会被 发送一次,即 At MostOnce 语义。
即:
ack=-1:At Least Once 不丢数据可能重复
ack=0:At Most Once 会丢数据,但不会重复
如何实现Exactly Once呢?
1、0.11版本之前
At Least Once + 下游系统自己处理幂等性=Exactly Once
2、0.11版本及以后
0.11版本引入了一项重大特性:幂等性。所谓的幂等性就是指 Producer 不论 向 Server 发送多少次重
复数据,Server 端都只会持久化一条。幂等性结合 At Least Once 语 义,就构成了 Kafka 的 Exactly
Once 语义。即:
At Least Once + 幂等性 = Exactly Once
要启用幂等性,只需要将 Producer 的参数中 enable.idompotence 设置为 true 即可。当true,acks默认all
3、幂等性的实现
Kafka 的幂等性实现其实就是将原来下游业务需要做的去重放在了数据上游。开启幂等性的 Producer
在 初始化的时候会被分配一个 PID,发往同一 Partition 的消息会附带 Sequence Number。而 Broker
端会对<PID, Partition, SeqNumber>做缓存,当具有相同主键的消息提交时,Broker 只 会持久化一条
但是 PID 重启就会变化,同时不同的 Partition 也具有不同主键,所以幂等性无法保证跨分区跨会话的
Exactly Once。
特别注意:所谓Exactly Once就是生产者发给Kafka多少数据,Kafka刚刚不多不少的存储多少数据。
至于Kafka能不能保证Exactly Once都根消费者没关系。
基于At Least Once + 幂等性(下游或者kafka自己实现)无法实现跨会话的Exactly Once