Kafka设计(7)复制

参考文档: http://kafka.apache.org/documentation/#design

复制

Kafka跨越指定配置数量服务器(可以设置这个复制因子在)复制每个主题分期的日志。这提供了自动故障转移功能,当集群中一个服务器宕机时消息依旧可用。

其他消息系统提供了复制相关的功能,但在我们看来(完全有偏见),此功能像是附加功能,没有被大量使用,并有很大的缺点:副本处于非活动状态,吞吐量受到严重影响,需要精细的手动配置等等。Kafka原本就默认和复制一同工作(实际上我们实现了非复制主题,只需要将复制因子设置为1)。

复制的单元是主题分区,在无故障状态下,每个分区有一个leader和0或多个follower。复制因子是所有副本的数量(包括leader)。所有读写都导向到leader分区。通常,分区的数量比broker更多,不同分区的leader在broker间均匀分布。follower节点的日志和leader一致,都有同样的偏移量和相同的消息及其顺序(当然,在某一时刻,leader可能有一些处于日志尾端的消息未复制到follower)。

Follower从leader像一个普通的Kafka消费者一样消费消息,并将消息写到自己的日志中。让follower从leader拉取消息允许follower批量获取消息并批量写入本地日志。

大多数自动容错的分布式系统需要精确定义如何判断一个节点是否存活。Kafka中存活节点必须满足两个状态

  • 一个节点必须能够保持和Zookeeper的会话(通过Kafka心跳机制)
  • 如果是follower节点,那它必须复制leader上发生的写入并不能落后太远。

我们将满足以上两种状态的节点标记为“in sync”,以和“alive”和“failed”区分开。leader记录所有“in sync”节点,如果follower死亡、卡住或者落后太多,leader将从“in sync”集合中将其移除。可通过 replica.lag.time.max.ms配置复制的最大延迟。

在分布式系统术语中,我们仅尝试处理“失败/恢复”故障模型(节点突然停止工作并过一段时间恢复,很可能都不知道他们曾经宕机)。Kafka不处理“ "Byzantine"故障(节点产生任意或恶意响应,可能因bug或不当行为产生)。

我们现在可以跟精准定义消息在分区的所有同步副本都写入到日志时才被认为是已提交。只有已提交消息才会被消费者消费。

  • 消费者无需担心看到一个消息后因leader宕机造成该消息丢失。
  • 生产者可以基于延迟和持久性进行权衡,来配置是否等待消费被确认。可以在生产者的响应设置中配置。

主题可以设置处于“in sync”状态副本的最小数量,当生产者请求确认消息已经被写入到所有“in sync”副本中时将检查该设置。如果生产者请求的是不严格的确认,那么即使“in sync”状态副本数低于最小数量(例如只剩下leader),消息也能被确认和消费。

Kafka保证已提交的消息不会丢失,在所有时刻都至少有一个同步副本存活。

当出现节点失败时,经过短暂的故障转移阶段Kafka仍旧保持可用,但当出现网络分区故障时Kafka可能无法继续可用。

复制日志:法定人数、ISR(同步副本)和状态机

kafka分区的核心是复制日志。复制日志是分布式数据系统的基本原语之一,并有很多实现方法。其他系统可以将分区日志作为原语以状态机风格来实现其他分布式系统。

复制日志对一系列按顺序的值(通常对日志条目进行编号0、1、2,…)达成一致的处理进行建模。有很多实现方法,最简单快速的方法是由leader选择提供给他的值顺序。只要leader存活,所有follower只需要拷贝leader的值和leader选择的顺序即可。

当前如果leader不宕机我们不需要follower。当leader宕机,我们需要从follower中选择一个新leader。但follower可能落后leader或者宕机,因此我们必须确保选择最新的follower。如果我们告诉客户端消息已确认,同时leader宕机,选举的新leader必须同时拥有这个消息,这是日志复制算法最基本的保证。这产生一个折衷:如果leader在宣布消息已确认前等待越多的follower确认消息,就有越多的潜在的可被选举的领导者。

如果你选择选举leader所需确认的数量和被比较的日志数量(以确保重叠),这被称作法定人数(Quorum)。

一种常见的权衡办法是对消息确认决策和leader选择都使用多数投票。这不是Kafka做,但让我们探讨一下以理解这个权衡。假设有2f+1个副本,leader宣称消息被确认前需要f+1副本接受到消息,则我们在选举新leader需要从至少f+1个副本中选择拥有最完整日志的节点,在不超过f个节点故障时,至少有一个副本拥有所有已提交消息。这是因为在任意f+1个副本,至少有一个副本拥有所有完整消息,这个副本的日志是最完整的因此将被选择成为新leader。每个算法都还有很多细节需要处理(比如精确定义如何让日志更完整,当leader宕机时或复制集中服务器变更时确保日志一致性),不过我们先忽略这些。

多数票法有很好的特性:延迟依赖于最快的服务器。如果复制因子是3,延迟由最快的服务器而不是最慢的的决定。

在多数票法家族有大量的各类算法,包括ZooKeeper的ZabRaftViewstamped Replication。我们知道的和Kafka实现最相似的学术出版算法是微软的PacificA

多数投票法的劣势是如果没有可选举的leader,不会产生很多失败。为了容忍一个节点故障需要3个副本,容忍2个节点故障需要5个副本。根据我们的经验,对于实际的系统来说只能容忍单个故障是不够的,但每个写入进行5次,需要5倍磁盘空间和1/5的吞吐量,对于大量数据并不实际。这可能是仲裁算法更常见于共享配置集群(例如Zookeeper)但很少用于主要数据存储的原因。例如在HDFS中,namenode的高可用功能构建在基于多数票的日志上,但因太昂贵并没有用在本身的数据节点存储上。

Kafka采用略不同与多数票的方法来选择法定人数(quorum)。Kafka动态维护跟上leader的同步副本(ISR:in sync replicas)集。只有这个集合中的成员有资格成为leader。写入到Kafka分区直到所有同步副本(ISR)都接收到这个写入才被认做已确认。同步副本(ISR)集只要改变就持久化到Zookeeper上。因此,同步副本(ISR)中的所有副本都有资格被选举为领导者。当有很多分区场景时,这是kafka的重要因子,确保领导者(PS:在不同服务器间)平衡也很重要。基于同步副本(ISR)模型和f+1个副本,Kafka主题可以容忍f个故障并不丢失已提交的数据。

对于我们希望处理的大多数用例,我们任务这个权衡是合理的。实践上,为了容忍f个故障,多数票和同步副本(ISR)都需要在确认消息已提交前等待同样数量副本确认(例如,容忍一个节点故障,多数票需要3个副本和1个确认,同步副本需要2个副本和1个确认)。多数票法具有确认提交并不需要等待最慢服务器的能力。但是,我们认为可以通过允许客户端选择是否阻止消息提交来解决此问题,并且所需的较低复制因子带来的额外吞吐量和磁盘空间是值得的。

另一个重要设计区别是Kafka不需要故障节点完整恢复所有数据。依赖于稳定存储的复制算法并不少见,这种稳定存储在任何故障恢复场景中都不会丢失,并不会潜在地违反一致性。这个假设有两个主要问题:

  • 首先,我们观察到在数据持久化系统的实际操作中磁盘错误是很常见的故障,这些错误通常不会保证数据完整。
  • 其次,即使没有问题,我们不希望每次写入磁盘都使用fsync以保证一致性却降低性能2到3个量级,我们的协议允许副本重新加入

磁盘错误是我们在持久性数据系统的实际操作中观察到的最常见问题,它们通常不会使数据完整无缺。其次,即使这不是问题,我们也不希望在每次写入时都使用fsync来保证一致性,因为这会使性能降低2到3个数量级。我们的允许副本重新加入ISR的协议可确保在重新加入同步副本(ISR),只要在重新加入前副本已经完整重新同步,即使它在故障中丢失了未刷新到磁盘的数据。

PS:这个机制设计优缺点

  • 优点:充分利用操作系统buffer机制,批量写入磁盘提升性能
  • 缺点1:Kafka的同步副本并不能保证所有成员都有最新数据,因为可能会flush到磁盘失败。但Kafka通过多个同步副本解决这个问题,在选举时还要在同步副本中找到数据最新的节点,所有同步副本都落盘失败概率很低。
  • 缺点2:这也导致follower不能承担读负载,因为其数据无法保证和leader一致。。
  • 疑点:Kafka选举新leader时,应该在找到有最新数据的节点时需要确认数据是否落盘,否则可能会出现问题?

不明确的leader选举:全挂了怎么办

注意,Kafka关于数据丢失的保证前提是由最少一个同步状态的副本。如果分区中所有复制节点都宕机,这个保证不复存在。

当所有副本宕机时,实际系统需要做些事情。如果你不幸遇到这种情况,搞清楚发生了什么很重要,有以下两个方案可选:

  1. 等待同步副本(ISR)中的副本自己恢复并选择这个副本作为leader(祈祷它还有全部数据吧)
  2. 选择第一个恢复的副本(不一定在ISR中)作为leader。

这是在可用性和持久性间的简单权衡。如果我们等待ISR中的副本,只要这些副本未恢复,集群都将保持不可用。如果副本被损坏或者数据丢失,我们将永远瘫痪。另一方面,如果不在ISR的副本恢复,我们将其作为leader,它的日志成为基准,虽然它无法保证拥有所有已提交消息。默认从0.11.0.0版本起,Kafka选择第一个策略更倾向与为一致性副本而等待。通过unclean.leader.election.enable配置可以改变这个行为,以支持恢复时间比一致性重要的场景。

这个困境不是Kafka特有的,所有基于法定人数的模式都存在。例如在多数票模式下,如果多数服务器都永久性故障,你必须选择丢失100%数据或者违反一致性选择可用的服务器作为新基准。

可用性和持久性保证

当写入到Kafka时,生产者可以选择是否等待消息被0、1或者所有副本确认。注意,“所有副本确认”并不保证所有副本都收到消息。默认,当acks=all,所有当前处于同步状态的副本收到消息时发布确认。例如,如果主题配置有2个副本,一个宕机,只剩下一个同步副本,配置为acks=all的写入将会成功。然而,当同步副本也宕机时这个写入可能丢失。尽管这确保了分区的最大可用性,但是对于某些倾向于持久性而不是可用性的用户而言,不希望看到此行为。因此,我们提供2个顶层配置用于倾向于持久性而不是可用性。

  1. 禁用不明确的领导者选举,如果所有副本变得不可用,分区将保持不可用直到最新的leader变得重新可用。这将在磁盘数据丢失时导致不可用。具体参见上一节。
  2. 指定最小ISR大小,分区只在ISR成员数量大于指定最小值时才会接受写入,以阻止在ISR只有一个副本时因该副本故障丢失消息。这个设置只在生产者设置acks=all时生效,并保证至少指定数量的同步副本确认。这个设置提供了在一致性和可用性间的权衡,更高的值保证更好的一致性因为消息保证写入到更多的副本降低了丢失的可能。然而降低了可用性,如果ISR数量低于该值将无法写入。

复制管理

以上复制日志的讨论实际上只是针对一个日志,例如,一个主题分区。然而Kafka集群可能管理成百上千个分区。我们尝试采用轮询模式平衡集群中的分区以避免将大数量主题的所有分区分布到少量节点。同样我们尝试平衡leader以便每个节点都按比例的分配分区的leader(PS:leader承受更大的写入读取负担)。

优化leader选举过程同样很重要,因为它决定了不可用的关键时间窗口。一个简陋的领导者选举实现将在节点故障时为节点上所有分区的每个分区运行一个选举。取而代之,我们选举一个broker节点作为控制器(controller)。这个控制器在broker级别探测故障并负责改变所有影响分区的leader。这样我们能将所需的很多领导者变更通知一起处理,让大量分区的选举过程更轻便快速。如果控制器宕机,幸存的一个broker将成为新控制器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值