架构师之路-如何建立高可用消息中间件kafka?( 六 )


如果replica-0不能恢复 。需要将min.insync.replicas设置为1 。恢复write功能 。
当ISR中的replica-0出现crash 。紧接着replica-1也出现了crash, 此时[ISR=(1),leader=-1],不能对外提供服务 。此种情况恢复方案:
尝试恢复replica-0和replica-1 。如果都能起来 。则系统恢复正常;
如果replica-0起来 。而replica-1不能起来 。这时候仍然不能选出leader 。因为当设置unclean.leader.election.enable=false时 。leader只能从ISR中选举 。当ISR中所有副本都失效之后 。需要ISR中最后失效的那个副本能恢复之后才能选举leader, 即replica-0先失效 。replica-1后失效 。需要replica-1恢复后才能选举leader 。保守的方案建议把unclean.leader.election.enable设置为true,但是这样会有丢失数据的情况发生 。这样可以恢复read服务 。同样需要将min.insync.replicas设置为1 。恢复write功能;
replica-1恢复 。replica-0不能恢复 。这个情况上面遇到过 。read服务可用 。需要将min.insync.replicas设置为1 。恢复write功能;
replica-0和replica-1都不能恢复 。这种情况可以参考情形2.
当ISR中的replica-0, replica-1同时宕机,此时[ISR=(0,1)],不能对外提供服务 。此种情况恢复方案:尝试恢复replica-0和replica-1 。当其中任意一个副本恢复正常时 。对外可以提供read服务 。直到2个副本恢复正常 。write功能才能恢复 。或者将将min.insync.replicas设置为1 。
3.7 Kafka的发送模式
Kafka的发送模式由producer端的配置参数producer.type来设置 。这个参数指定了在后台线程中消息的发送方式是同步的还是异步的 。默认是同步的方式 。即producer.type=sync 。如果设置成异步的模式 。即producer.type=async 。可以是producer以batch的形式push数据 。这样会极大的提高broker的性能 。但是这样会增加丢失数据的风险 。如果需要确保消息的可靠性 。必须要将producer.type设置为sync 。
对于异步模式 。还有4个配套的参数 。如下:

架构师之路-如何建立高可用消息中间件kafka?

文章插图
以batch的方式推送数据可以极大的提高处理效率 。kafka producer可以将消息在内存中累计到一定数量后作为一个batch发送请求 。batch的数量大小可以通过producer的参数(batch.num.messages)控制 。通过增加batch的大小 。可以减少网络请求和磁盘IO的次数 。当然具体参数设置需要在效率和时效性方面做一个权衡 。在比较新的版本中还有batch.size这个参数 。
4 高可靠性使用分析4.1 消息传输保障
前面已经介绍了Kafka如何进行有效的存储 。以及了解了producer和consumer如何工作 。接下来讨论的是Kafka如何确保消息在producer和consumer之间传输 。有以下三种可能的传输保障(delivery guarantee):
At most once: 消息可能会丢 。但绝不会重复传输
At least once:消息绝不会丢 。但可能会重复传输
Exactly once:每条消息肯定会被传输一次且仅传输一次
Kafka的消息传输保障机制非常直观 。当producer向broker发送消息时 。一旦这条消息被commit 。由于副本机制(replication)的存在 。它就不会丢失 。但是如果producer发送数据给broker后 。遇到的网络问题而造成通信中断 。那producer就无法判断该条消息是否已经提交(commit) 。虽然Kafka无法确定网络故障期间发生了什么 。但是producer可以retry多次 。确保消息已经正确传输到broker中 。所以目前Kafka实现的是at least once 。
consumer从broker中读取消息后 。可以选择commit 。该操作会在Zookeeper中存下该consumer在该partition下读取的消息的offset 。该consumer下一次再读该partition时会从下一条开始读取 。如未commit 。下一次读取的开始位置会跟上一次commit之后的开始位置相同 。当然也可以将consumer设置为autocommit 。即consumer一旦读取到数据立即自动commit 。如果只讨论这一读取消息的过程 。那Kafka是确保了exactly once, 但是如果由于前面producer与broker之间的某种原因导致消息的重复 。那么这里就是at least once 。
考虑这样一种情况 。当consumer读完消息之后先commit再处理消息 。在这种模式下 。如果consumer在commit后还没来得及处理消息就crash了 。下次重新开始工作后就无法读到刚刚已提交而未处理的消息 。这就对应于at most once了 。
读完消息先处理再commit 。这种模式下 。如果处理完了消息在commit之前consumer crash了 。下次重新开始工作时还会处理刚刚未commit的消息 。实际上该消息已经被处理过了 。这就对应于at least once 。
要做到exactly once就需要引入消息去重机制 。
4.2 消息去重
如上一节所述 。Kafka在producer端和consumer端都会出现消息的重复 。这就需要去重处理 。

推荐阅读