08-Kafka可靠性

Kafka可靠性

  • Kafka使用多副本机制来实现水平扩展、容灾和高可用性以及可靠性。

Kafka系列文章是基于:深入理解Kafka:核心设计与实践原理一书,结合自己的部分实践和总结。

一、副本

1.1 引入

1.1.1 分类
  • 副本:分布式系统对数据和服务提供的一种冗余方式。
  • 数据副本:在不同节点持久化同一份数据来解决分布式系统数据丢失的问题。
  • 服务副本:多节点提供同样的服务。
1.1.2 黄金定律
  • 任何在设计阶段考虑到的问题或者异常情况,一定会在系统实际运行中发生,并且还会遇到很多设计未考虑到的异常故障。
1.1.3 概念
  • 副本是针对分区而言,一个分区可以包含一个或者多个副本。
  • 如果有多个副本,其中一个为leader副本其余为follower副本,leader负责提供读写请求,follower只同步leader的数据
  • 所有副本集合称为AR,与leader保持同步状态的副本集合为ISR,leader也是ISR的一员
  • LEO标示每个分区中最后一条消息的下一个位置,每个分区都有LEO,ISR中的最小LEO则为HW,HW之前的消息才能被消费者拉去。(HW称为高水位)

1.2 副本失效

  • AR减去ISR则为失效副本集合。失效副本包括同步失效和功能失效,前者可能是同步慢了,后者可能是不可用了。
1.2.1 副本失效
  • 查看某个topic下包含失效副本的分区:
PS::./kafka-topics.sh --zookeeper localhost:2181 --describe --topic ifaas-target --under-replicated-partitions

注意:这里的失效是包含功能失效和同步失效 
1.2.2 同步失效(replica.lag.time.max.ms)
  • 同步失效的副本会被踢出ISR集合,kafka broker端通过参数replica.lag.time.max.ms来指定如果一个follower副本滞后leader的时长达到该配置,则踢出ISR,认为同步失效。
replica.lag.time.max.ms配置默认10000,即10S

PS:注意即使follower副本不断拉取leader的数据,也有可能是失效的,因为其拉取速度有可能跟不上leader的写入速度

另外新增副本时,新增的副本在赶上leader之前都是失效的
  • 同步失效可能原因
1.follower副本进程被卡主,向leader发起拉取数据的请求频率不够,比如由于full gc导致
2.fillower副本进程同步过慢,无法赶上leader,比如IO开销过大
这两个可能的原因是Kafka源码中的注释说明
  • 具有失效副本的分区数量是一个很重要的衡量Kafka的指标,它意味着一个topic下的这些分区可能存在较大的时延或者性能隐患。

1.3 ISR伸缩

1.3.1 ISR缩减
  • kafka启动后会开启两个与ISR相关的定时任务,分别为:isr-expiration和isr-change-propagation。

  • isr-expiration: isr-expiration会周期性的检查是否需要缩减ISR集合,其频率是replica.lag.time.max.ms的一半,默认5000,当检测到ISR集合中有副本失效时,就会将其踢出ISR集合,并且会把新的ISR副本集合写入zk的/brokers/topics/topicName/partition/partitionName/state节点,内容如下:

[zk: localhost:2181(CONNECTED) 0] get /brokers/topics/ifaas-target/partitions/0/state
{"controller_epoch":1,"leader":1,"version":1,"leader_epoch":0,"isr":[1]}
cZxid = 0x108
ctime = Wed Jun 19 06:27:47 UTC 2019
mZxid = 0x108
mtime = Wed Jun 19 06:27:47 UTC 2019
pZxid = 0x108
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 72
numChildren = 0
[zk: localhost:2181(CONNECTED) 1] 

PS:
controller_epoch:控制器broker的brokerId
leader:当前分区的leader副本所在的节点brokerId
version:固定为1
leader_epoch:当前分区的leader纪元(即第几代leader)
isr:ISR集合列表
  • isr-change-propagation:isr-expiration 检测到ISR副本收缩时,还会将新的ISR副本集合写入isrChangeSet集合,isr-change-propagation会周期性的(默认2500ms)检查该集合,如果有变化,则会在zk的/isr_change_notification路径下面创建一个顺序持久节点,并将新的ISR集合信息保存到该节点;
  • kafka的控制器会监听/isr_change_notification节点获取新的ISR集合信息然后更新元数据信息,并通知集群中的其他broker节点,最后删除该路径下已经处理过的节点。
频繁的将新的ISR变更信息写入zk节点会过多的触发控制器的Watch机制影响性能,因此在检测ISR集合变更的时候,还会附加检测条件,需要满足下面二者之一才行:
1.上一次ISR集合发生变化距离现在已经超过5S
2.上一次写入zk的时间距离现在已经超过60S
1.3.2 ISR扩展
  • 落后的follower副本如果跟上leader副本,则会进入ISR集合。注意ISR扩展时也会更新zk的/brokers/topics/topicName/partition/partitionName/state节点和isrChangeSet集合,后面的步骤和收缩一样。

  • 跟上的标准是follower的LEO是否小于leader的HW。如果follower的LEO小于该分区的HW,那么他就不能进入ISR集合,反之大于则可以进入。(大于的时候,可以粗略理解为这个follower副本比ISR中的某些副本还要更接近leader,因此它当然有理由进入ISR,反之小于则表示它比ISR中的全部副本都要落后,因此不能进入ISR)

1.ISR集合变化时,或者ISR中任意一个副本的LEO发生变化时,都有可能影响整个分区的HW。(HW = min(分区ISR副本的LEO))

1.4 LEO与HW

  • 细节阅读参考文章[1]的8.1.3小节。

1.5 Leader与Epoch介入

  • kafka的0.11.0.0之前的版本,分区副本之间的同步机制是基于HW,但是有丢失数据或者leader和follower副本数据不一致的问题。在0.11.0.0版本开始引入了leader epoch来解决数据丢失和不一致的问题,细节阅读参考文章[1]的8.1.4小节。

1.6 为什么不支持读写分离?

  • leader分区副本负责该分区的读写,因此kafka并不支持主写从读这样的读写分离模式。
1.代码复杂
2.数据一致性问题
3.延时问题
  • 分区:kafka通过分区机制让不同的分区的leader副本在不同的broker节点来实现负载均衡,很多时候优于主写从读。不过也有一下的因素可能导致负载不均衡
1.broker上分区不均匀;部分节点分区多,部分节点分区少;
2.生产者写入不均匀;不同的分区写入的负载不同
3.消费者消费不均匀;同2
4.leader切换不均,比如broker宕机导致leader转移,最后导致leader分区在broker上分布不均;

二、日志同步

  • 日志同步机制需要保证数据一致性和数据顺序性,其基本原则是:如果响应给客户端消息已经提交成功,那么即使leader宕机也要确保新选举的leader包含该消息。因此这需要权衡,如果leader在响应消息已经被提交之前需要更多的follower确定势必影响性能。
  • Kafka采用ISR的机制来同步日志,在需要相同确认信息数的情况下比常见的“少数服从多数”的机制所需要的副本数要少。

三、可靠性分析

  • 在Kafka本身的角度,如何提高可靠性?

3.1 副本

  • 多副本;代价是存储,IO的浪费,一般3副本足够,特殊场景5副本,引入机架信息则需要考虑机架整体宕机。

3.2 acks

  • acks=1的场景是有丢失消息的可能,在设置acks=all的场景可以保证消息被写入ISR集合(ISR集合可能只有leader,因此这还不足以保证任何时候的可靠性)。
  • min.insync.replicas
部分场景会让ISR退化为只有leader,那么此时acks=all等价于acks=1,比如其他副本所在broker故障或者leader写入非常快导致所有的follower都跟不上同步的速度,此
时结合min.insync.replicas配置和acks一起使用,该配置指定了ISR集合的最小副本数,(当然首先副本数要不小于这个值)。如果ISR集合小于该配置则会抛出特定的异常
(NotEnoughReplicasException或者NotEnoughReplicasAfterAppendException)。比如副本数为3,min.insync.replicas配置为2。不过该配置会一定程度影响可用性,假设
不配置的话ISR即使只有leader起码还能用,配置了之后只有leader就不能用了。
  • unclean.leader.election.enable(默认false)
该配置也是可靠性和可用性的一个折中配置,默认false表示leader故障之后选举只能从ISR中选举,不能从非ISR集合选举新的leader。配置为false显然是选择了可靠性,因为
置为true的话如果ISR只剩leader而leader又故障了那么起码还能从非ISR中选一个顶上来,但是那样很可能有数据丢失的风险,因为非ISR是落后于leader的。置为false此时就
不可用了,但是不会有数据丢失。

3.3 刷盘

  • 同步刷盘是增强可靠性的一个策略但是却极其损耗性能,分布式组件应该通过副本机制来保证可靠性。kafka中通过log.flush.interval.messages和log.flush.interval.ms这两个配置来调整刷盘策略,默认是不做控制交给操作系统来刷盘。

3.4 发送模式

  • 需要提升可靠性的场景,应该使用同步或者异步的方式发送消息,在发送失败的时候进行补救,比如重试。不应该选择发后即忘的模式。(参考:02-Kafka生产者 1.3.2小节)

3.5 重试和顺序

  • 在开启重试的情况下可能会导致消息乱序,此时要么放弃重试要么放弃吞吐。(参考:02-Kafka生产者 2.3.3小节)
1.放弃重试;不建议放弃重试,因为有些异常是可以恢复的,重试可以让消息发送成功
2.放弃吞吐;将配置max.in.flight.requests.per.connection参数设置为1,表示每个节点未收到响应的最大消息缓存数量,置为1意味着1条消息未收到响
应,则不会发下一条消息,会对吞吐有影响;(默认为5)

3.6 消费者

  • 如果消费者因为某些原因未能消费到某条数据,那么对于应用而言该条消息也是丢失的。
  • enable.auto.commit(true)
默认开启自动提交,编码方便,但是有重复消费和数据丢失的风险。

手动提交模式,对于高可靠性要求的应用要确保一条消息一定被消费成功之后在提交对应消息的offset,对于可能的重复消费提交可以通过其他机制来避免消息重
复解析,如果消息反复解析失败导致不能提交,可以考虑将消息加入死信队列(参考后续文章)
  • Kafka消费端还能够进行消息回溯来进行最终的历史消息处理,对漏掉的消息进行一下弥补措施。

四、总结

  • 关于可靠性部分的介绍

五、参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值