之前我分享了一个视频,《分布式系统中的强一致性和弱一致性》,我们来看看Kafka是怎么保证一致性的。
权衡 trade-offs
鱼和熊掌不可兼得。系统设计需要根据具体的应用场景做出权衡。
- 吞吐量 - 每分钟可以写入多少消息,可以消费多少消息?
- 可用性 - 可以同24x7的不间断服务吗?
- 可靠性 - 写入的消息,可以保证顺序不变吗?保证不丢吗?
- 一致性 - 确保所有的消费者都能读到一样的消息吗?
- 低延迟 - 写入和读出消息的响应时间是几毫秒?
- 磁盘空间 - 需要多大的磁盘空间来存储这些消息?
系统设计者可以通过配置Kafka,来得到不同程度的需求满足。
主从副本 Replication
每个Kafka主题(topic)都分为多个分区(partitions)。每个分区可以具有多个副本(replica),其中一个副本是主分区(leader)。所有读写请求都由主分区提供。其他副本只需要与主分区(leader)保持同步(in-sync),并实时复制所有最近的消息。如果主分区挂了,则一个同步副本(in-sync)将成为新的主分区(leader)。从而是整个集群继续可用。
Kafka的主从副本(每个分区具有多个副本)是Kafka所有可靠性保证的核心。在多个副本中写入消息是,Kafka可以在某个节点发生崩溃时也能保证消息不丢。
什么是同步副本(in-sync)?
除了主分区(leader),满足以下条件的分区副本也是同步副本(in-sync):
- 与Zookeeper保持会话(session) - 最近6秒钟(可配置)内向Zookeeper发送了心跳
- 最近10秒钟内(可配置)从主分区获取过所有的最新消息 - 最多比lead少10s的消息
不满足以上条件的分区副本就是不同步的(out-of-sync)。有可能是节点挂掉从启动了,也有可能是网络断了一段时间。当不同步的副本赶上之后,满足了上面的两个条件,就也会成为同步分区(in-sync)。 这是一个动态变化的过程。
![de913b26f696d073600fc7a528f0ff39.png](https://i-blog.csdnimg.cn/blog_migrate/040c253c1bc7b8ad89873dcb7a6c1b2a.jpeg)
如果某个in-sync的分区比较慢,会影响整个集群的相应速度,因为producer需要等所有的in-sync的副本确认提交(commit)消息,才认为成功发送;consumer也只能读到提交(commit)的消息。反而out-of-sync的分区不会影响性能,因为它不参与决策。
服务器端的配置
服务器broker中的三个配置参数可影响Kafka关于可靠消息存储的行为。
- replication.factor,副本个数,默认是3个;每个topic可以有不同的配置,也可以运行时修改。
- min.insync.replicas,最少的in-sync的节点数,默认是2。可以在broker和topic级别上配置。
- unclean.leader.election.enable, 允许out-of-sync的分区成为lead吗?默认是true(不安全)
副本个数 replication.factor的权衡
副本个数越多,自然是越安全,但是也会越慢。因为需要同步的节点多。同时需要的磁盘空间也大。broker的数量要大于副本个数,在一个broker上有多于1个的副本没有意义。
如果对一致性要求很高,比如金融系统,可以设置为5。如果是日志收集或者性能监控消息收集,可以允许丢消息,消息量很大,那么1或者2都可以。
min.insync.replicas 最少的in-sync的节点数
只有所有in-sync的节点都commit了,消息才算提交了(commit)。如果总的replica是3个,min.insync.replicas 是2,那么是允许1个replica挂掉的。这样只要lead还有一个备份。当然你也可以选择全部必须都是in-sync的,那样不允许一个replica挂掉。
unclean.leader.election.enable, 允许out-of-sync的分区成为lead吗?默认是true
如果 min.insync.replicas 小于 replication.factor,就意味着允许有out-of-sync的副本存在。比如默认是3个备份,最小in-sync的节点数是2的情况下,就可能有一个replica是out-of-sync的。这个时候如果lead挂了,那么还有一个in-sync的replica可以当lead,还是安全的。但是如果lead和in-sync的节点都挂了,只剩下一个out-of-sync的节点了,怎么办?
允许这个节点成为主lead节点吗?这个决定很重要,关系到可用性和可靠性还有一致性的选择:
如果允许,那么就有可能丢消息,因为这个节点不包含很多已经提交的(commit)的消息,而哪些已经提交的消息很可能已经被消费过了。比如图中,消费者已经消费到offset 900了,那个out-of-sync的节点才到350。这就是说这个新lead会产生新的从351以后的消息。如果别的消费者去消费,就会消费到和之前的消费者不一样的消息。这样就不一致了。
如果不允许,那就必须停止对外服务。这个cluster挂掉了。得等那两个in-sync的节点恢复好,这可能需要比较长的时间。会影响系统的可用性。
总结
整个系统的一致性和可靠性也不完全是服务器broker端可以决定的。客户端在生产消息和消费消息的时候也可以做出选择,也需要做出一个配合。我们下次继续分享。
关注的多的话我会尝试做成动画来演示一下整个过程。