RabbitMQ 与 Kafka:容错性和高可用性

RabbitMQ 与 Kafka:容错性和高可用性

В 上一篇文章 我们研究了 RabbitMQ 集群的容错性和高可用性。 现在让我们深入研究 Apache Kafka。

这里的复制单位是分区。 每个主题都有一个或多个部分。 每个部分都有一个领导者,有或没有追随者。 创建主题时,您指定分区数和复制系数。 通常值为 3,表示三个副本:一个领导者和两个追随者。

RabbitMQ 与 Kafka:容错性和高可用性
米。 1. 四个部分分布在三个经纪商之间

所有的读写请求都会发送给领导者。 Followers 定期向 Leader 发送请求以接收最新消息。 消费者永远不会转向追随者;追随者的存在只是为了冗余和容错。

RabbitMQ 与 Kafka:容错性和高可用性

分区失败

当一个经纪人失败时,几个部门的领导者往往都会失败。 在每个节点中,来自另一个节点的追随者成为领导者。 事实上,情况并非总是如此,因为同步因素也会影响:是否有同步的跟随者,如果没有,则是否允许切换到不同步的副本。 但现在我们不要让事情复杂化。

经纪人 3 离开网络,经纪人 2 为第 2 部分选举了一位新的领导者。

RabbitMQ 与 Kafka:容错性和高可用性
米。 2. Broker 3 死亡,其在 Broker 2 上的追随者被选为分区 2 的新领导者

然后,经纪人 1 离开,部分 1 也失去了领导者,其角色转移到经纪人 2。

RabbitMQ 与 Kafka:容错性和高可用性
米。 3. 还剩一名经纪人。 所有领导者都在一个经纪人上,零冗余

当代理 1 重新上线时,它会添加四个追随者,为每个分区提供一些冗余。 但所有领导者仍然留在经纪人 2 上。

RabbitMQ 与 Kafka:容错性和高可用性
米。 4. 领导者仍留在经纪商 2 上

当代理 3 出现时,我们又回到每个分区三个副本。 但所有领导者仍然在经纪人 2 上。

RabbitMQ 与 Kafka:容错性和高可用性
米。 5. 经纪商 1 和 3 恢复后领导者安置不平衡

Kafka 有一个比 RabbitMQ 更好的领导者再平衡工具。 在那里,您必须使用第三方插件或脚本,通过减少迁移期间的冗余来更改迁移主节点的策略。 此外,对于大型队列,我们​​必须接受同步期间不可用的情况。

Kafka 对于领导者角色有“首选副本”的概念。 创建主题分区时,Kafka 会尝试在节点之间均匀分布领导者,并将这些第一个领导者标记为首选。 随着时间的推移,由于服务器重新启动、故障和连接中断,领导者可能最终会出现在其他节点上,就像上面描述的极端情况一样。

为了解决这个问题,Kafka 提供了两种选择:

  • 选项 auto.leader.rebalance.enable=true 允许控制节点自动将领导者重新分配回首选副本,从而恢复均匀分布。
  • 管理员可以运行该脚本 kafka-preferred-replica-election.sh 用于手动重新分配。

RabbitMQ 与 Kafka:容错性和高可用性
米。 6. 重新平衡后的副本

这是失败的简化版本,但现实要复杂得多,尽管这里并没有什么太复杂的。 这一切都归结为同步副本(In-Sync Replicas,ISR)。

同步副本 (ISR)

ISR 是被视为“同步”(同步)的分区的一组副本。 有领导者,但不一定有追随者。 如果追随者在间隔到期之前准确复制了所有领导者的消息,则被认为是同步的 副本.延迟时间.最大.ms.

如果某个追随者出现以下情况,则会从 ISR 集中删除:

  • 没有提出选择间隔的请求 副本.延迟时间.最大.ms (推定死亡)
  • 期间未能更新 副本.延迟时间.最大.ms (认为​​很慢)

关注者在该时间间隔内提出采样请求 副本.fetch.wait.max.ms,默认为 500ms。

为了清楚地解释 ISR 的目的,我们需要查看生产者的确认和一些失败场景。 生产者可以选择经纪人何时发送确认:

  • acks=0,不发送确认
  • acks=1,领导者向本地日志写入消息后发送确认
  • acks=all,ISR中的所有副本都将消息写入本地日志后发送确认

在 Kafka 术语中,如果 ISR 保存了一条消息,那么它就是“已提交”。 Acks=all 是最安全的选项,但也会增加额外的延迟。 让我们看一下两个失败示例以及不同的“ack”选项如何与 ISR 概念交互。

Acks=1 和 ISR

在这个例子中,我们将看到,如果领导者不等待所有追随者的每条消息都被保存,那么如果领导者失败,数据可能会丢失。 可以通过设置启用或禁用导航到未同步的关注者 不干净的领导者选举启用.

在此示例中,制造商的值 acks=1。 该部分分布在所有三个经纪商中。 Broker 3 落后了,它在八秒前与领导者同步,现在落后了 7456 条消息。 经纪人 1 仅落后一秒。 我们的生产者发送一条消息并快速收到返回的确认,而不会产生领导者不等待的缓慢或死追随者的开销。

RabbitMQ 与 Kafka:容错性和高可用性
米。 7. 具有三个副本的 ISR

Broker 2 失败,生产者收到连接错误。 在领导权传递给代理 1 后,我们丢失了 123 条消息。 经纪商 1 上的追随者是 ISR 的一部分,但在下跌时与领导者并不完全同步。

RabbitMQ 与 Kafka:容错性和高可用性
米。 8.崩溃时消息丢失

配置中 引导服务器 制造商列出了多个经纪人,可以询问另一位经纪人谁是新的部门领导者。 然后它与代理 1 建立连接并继续发送消息。

RabbitMQ 与 Kafka:容错性和高可用性
米。 9. 短暂中断后恢复发送消息

Broker 3 甚至更落后。 它发出提取请求但无法同步。 这可能是由于代理之间的网络连接速度慢、存储问题等造成的。它已从 ISR 中删除。 现在 ISR 由一个副本组成 - 领导者! 制造商继续发送消息并接收确认。

RabbitMQ 与 Kafka:容错性和高可用性
米。 10. 经纪商 3 上的追随者已从 ISR 中删除

代理 1 宕机,领导角色转移到代理 3,并丢失 15286 条消息! 制造商收到连接错误消息。 过渡到 ISR 之外的领导者只有在以下情况下才可能实现: unclean.leader.election.enable=true。 如果它安装在 false,则不会发生转换,并且所有读写请求都将被拒绝。 在这种情况下,我们等待代理 1 返回副本中完整的数据,该副本将再次接管领导权。

RabbitMQ 与 Kafka:容错性和高可用性
米。 11. 经纪人 1 倒闭。 当发生故障时,大量消息丢失

制作人与最后一位经纪人建立了连接,并发现他现在是该部分的领导者。 他开始向经纪人 3 发送消息。

RabbitMQ 与 Kafka:容错性和高可用性
米。 12. 短暂休息后,消息再次发送到section 0

我们看到,除了建立新联系和寻找新领导者的短暂中断外,制造商还在不断发送消息。 此配置以牺牲一致性(数据安全性)为代价来确保可用性。 卡夫卡丢失了数千条消息,但继续接受新的写入。

Acks=全部和 ISR

让我们再次重复这个场景,但是 确认=全部。 代理 3 的平均延迟为四秒。 制造商发送一条消息 确认=全部,现在没有收到快速回复。 领导者等待 ISR 中所有副本保存消息。

RabbitMQ 与 Kafka:容错性和高可用性
米。 13. 具有三个副本的 ISR。 一是速度慢,导致录制延迟

经过四秒的额外延迟后,代理 2 发送一个确认。 所有副本现已完全更新。

RabbitMQ 与 Kafka:容错性和高可用性
米。 14.所有副本保存消息并发送ack

Broker 3 现在进一步落后并已从 ISR 中删除。 由于 ISR 中没有留下慢速副本,因此延迟显着减少。 经纪人 2 现在只等待经纪人 1,他的平均延迟为 500 毫秒。

RabbitMQ 与 Kafka:容错性和高可用性
米。 15. 代理 3 上的副本已从 ISR 中删除

然后代理 2 崩溃,领导权转移给代理 1,且不会丢失消息。

RabbitMQ 与 Kafka:容错性和高可用性
米。 16.经纪人2倒下

制造商找到了一位新的领导者并开始向他发送消息。 延迟进一步减少,因为 ISR 现在由一个副本组成! 因此选项 确认=全部 不增加冗余。

RabbitMQ 与 Kafka:容错性和高可用性
米。 17. Broker 1 上的副本领先且不丢失消息

然后代理 1 崩溃,领先者转到代理 3,丢失了 14238 条消息!

RabbitMQ 与 Kafka:容错性和高可用性
米。 18. 经纪人 1 死亡和领导层过渡与不干净的设置导致大量数据丢失

我们无法安装该选项 不干净的领导者选举启用 转化为意义 true。 默认情况下是相等的 false。 设置 确认=全部 с unclean.leader.election.enable=true 提供可访问性并增加一些数据安全性。 但正如您所看到的,我们仍然可能会丢失消息。

但如果我们想提高数据安全性怎么办? 你可以把 unclean.leader.election.enable = false,但这不一定能保护我们免受数据丢失的影响。 如果领导者摔倒并带走了数据,那么消息仍然会丢失,而且可用性也会丢失,直到管理员恢复情况为止。

最好确保所有消息都是冗余的,否则丢弃记录。 那么,至少从代理的角度来看,只有在两个或多个同时发生故障的情况下才可能丢失数据。

Acks=all、min.insync.replicas 和 ISR

带主题配置 最小同步副本数 我们正在提高数据安全级别。 让我们再次回顾一下上一个场景的最后部分,但这一次 min.insync.replicas=2.

因此,代理 2 有一个副本领导者,代理 3 上的追随者已从 ISR 中删除。

RabbitMQ 与 Kafka:容错性和高可用性
米。 19.来自两个副本的 ISR

代理 2 崩溃,领导权转移给代理 1,且不会丢失消息。 但现在 ISR 仅包含一个副本。 这不满足接收记录的最小数量,因此代理会以错误响应写入尝试 没有足够的副本.

RabbitMQ 与 Kafka:容错性和高可用性
米。 20. ISR的数量比min.insync.replicas中指定的少XNUMX

这种配置牺牲了可用性以换取一致性。 在确认消息之前,我们确保将其写入至少两个副本。 这让制造商更有信心。 在这里,只有当两个副本在很短的时间内同时失败,直到消息被复制到另一个跟随者时,消息才有可能丢失,而这是不可能的。 但如果你超级偏执,你可以将复制因子设置为 5,然后 最小同步副本数 3. 这里三个经纪人必须同时下跌才能失去记录! 当然,您要为这种可靠性付出额外的延迟代价。

当数据安全需要可访问性时

如在 RabbitMQ 案例,有时可访问性对于数据安全是必要的。 这是你需要考虑的:

  • 发布者是否可以简单地返回错误并让上游服务或用户稍后重试?
  • 发布者可以将消息保存在本地或数据库中以便稍后重试吗?

如果答案是否定的,那么优化可用性可以提高数据安全性。 如果您选择可用性而不是不记录,您会丢失更少的数据。 因此,一切都取决于找到一个平衡点,并根据具体情况做出决定。

ISR的含义

ISR 套件允许您选择数据安全性和延迟之间的最佳平衡。 例如,确保大多数副本发生故障时的可用性,最大限度地减少死副本或缓慢副本对延迟的影响。

我们自己选择意义 副本.延迟时间.最大.ms 根据您的需要。 本质上,这个参数意味着我们愿意接受多少延迟 确认=全部。 默认值为十秒。 如果这对您来说太长,您可以减少它。 然后,ISR 更改的频率将会增加,因为追随者将更频繁地被删除和添加。

RabbitMQ 只是一组需要复制的镜像。 慢速镜像会引入额外的延迟,而死镜像可以等到检查每个节点可用性(网络滴答)的数据包做出响应。 ISR 是避免这些延迟问题的一种有趣方法。 但我们面临失去冗余的风险,因为 ISR 只能收缩到领导者。 为了避免这种风险,请使用以下设置 最小同步副本数.

客户端连接保证

在设置中 引导服务器 生产者和消费者可以指定多个代理来连接客户端。 这个想法是,当一个节点出现故障时,会留下几个备用节点,客户端可以通过它们打开连接。 这些不一定是部门负责人,而只是初始加载的跳板。 客户端可以询问哪个节点托管读/写分区领导者。

在RabbitMQ中,客户端可以连接到任何节点,内部路由将请求发送到需要去的地方。 这意味着您可以在 RabbitMQ 前面安装负载均衡器。 Kafka 要求客户端连接到托管相应分区领导者的节点。 在这种情况下,您无法安装负载均衡器。 列表 引导服务器 客户端在发生故障后能够访问并找到正确的节点至关重要。

卡夫卡共识架构

到目前为止,我们还没有考虑集群如何获知 Broker 的故障以及如何选举新的领导者。 要了解 Kafka 如何处理网络分区,首先需要了解共识架构。

每个 Kafka 集群都与 Zookeeper 集群一起部署,Zookeeper 集群是一种分布式共识服务,允许系统就某些给定状态达成共识,优先考虑一致性而不是可用性。 读写操作需要获得大多数Zookeeper节点的同意。

Zookeeper存储集群的状态:

  • 主题、部分、配置、当前领导者副本、首选副本的列表。
  • 集群成员。 每个代理都会 ping Zookeeper 集群。 如果在指定时间内未收到 ping,Zookeeper 会将代理记录为不可用。
  • 选择控制器的主节点和备用节点。

控制节点是负责选举副本领导者的 Kafka 代理之一。 Zookeeper 向控制器发送有关集群成员身份和主题更改的通知,并且控制器必须对这些更改采取行动。

例如,我们采用一个具有 3 个分区且复制因子为 XNUMX 的新主题。控制器必须为每个分区选举一个领导者,尝试在代理之间最佳地分配领导者。

对于每个部分控制器:

  • 更新 Zookeeper 中有关 ISR 和领导者的信息;
  • 向托管此分区副本的每个代理发送 LeaderAndISRCommand,通知代理有关 ISR 和领导者的信息。

当具有领导者的代理失败时,Zookeeper 会向控制器发送通知,并选举新的领导者。 同样,控制器首先更新 Zookeeper,然后向每个代理发送命令,通知他们领导权变更。

每位领导者负责招募 ISR。 设置 副本.延迟时间.最大.ms 决定谁将进入那里。 当ISR发生变化时,Leader会向Zookeeper发送新的信息。

Zookeeper 始终会收到任何更改,以便在发生故障时,管理层可以顺利过渡到新的领导者。

RabbitMQ 与 Kafka:容错性和高可用性
米。 21.卡夫卡共识

复制协议

了解复制的详细信息有助于您更好地了解潜在的数据丢失情况。

采样查询、对数结束偏移 (LEO) 和高水位线 (HW)

我们认为追随者会定期向领导者发送获取请求。 默认间隔为 500 毫秒。 这与 RabbitMQ 的不同之处在于,RabbitMQ 中的复制不是由队列镜像发起的,而是由 master 发起的。 主服务器将更改推送到镜像。

领导者和所有追随者保存对数结束偏移 (LEO) 和高水位 (HW) 标签。 LEO 标记存储最后一条消息在本地副本中的偏移量,HW 保存最后一次提交的偏移量。 请记住,对于提交状态,消息必须在所有 ISR 副本中持久保存。 这意味着 LEO 通常略领先于 HW。

当领导者收到消息时,会将其存储在本地。 跟随者通过传输他的 LEO 来发出获取请求。 然后领导者从该 LEO 开始发送一批消息,并传输当前的 HW。 当领导者收到所有副本都已在给定偏移量处存储消息的信息时,它会移动 HW 标记。 只有领导者可以移动硬件,因此所有追随者都会知道对其请求的响应中的当前值。 这意味着追随者在消息和硬件知识方面可能落后于领导者。 消费者仅接收当前硬件的消息。

请注意,“持久化”意味着写入内存,而不是磁盘。 为了性能,Kafka 以特定的时间间隔同步到磁盘。 RabbitMQ 也有这样的间隔,但只有在主服务器和所有镜像都将消息写入磁盘后,它才会向发布者发送确认。 出于性能原因,Kafka 开发人员决定在消息写入内存后立即发送 ack。 Kafka 认为冗余可以抵消仅在内存中短暂存储已确认消息的风险。

领导失败

当领导者失效时,Zookeeper 会通知控制器,并选择一个新的领导者副本。 新领导者根据他的 LEO 设置了新的 HW 标记。 然后追随者会收到有关新领导者的信息。 根据 Kafka 的版本,follower 将选择以下两种场景之一:

  1. 它将截断本地日志到已知的硬件,并向新的领导者发送请求以获取此标记后的消息。
  2. 会向leader发送请求,找出当选leader时的HW,然后将日志截断到这个offset。 然后它将开始从该偏移量开始发出定期获取请求。

由于以下原因,关注者可能需要截断日志:

  • 当领导者失败时,向 Zookeeper 注册的 ISR 集中的第一个追随者将赢得选举并成为领导者。 ISR 上的所有追随者虽然被认为“同步”,但可能并未收到前领导者发送的所有消息的副本。 特色关注者完全有可能没有最新的副本。 Kafka 确保副本之间不存在分歧。 因此,为了避免差异,每个追随者必须将其日志截断为新领导者选举时的 HW 值。 这是设置的另一个原因 确认=全部 对于一致性非常重要。
  • 消息会定期写入磁盘。 如果所有集群节点同时发生故障,则具有不同偏移量的副本将存储在磁盘上。 当经纪人重新上线时,当选的新领导者可能会落后于他的追随者,因为他先于其他人保存到磁盘。

与集群重聚

当重新加入集群时,副本会执行与领导者失败时相同的操作:它们检查领导者的副本并将其日志截断到其硬件(在选举时)。 相比之下,RabbitMQ 同样将重新组合的节点视为全新的节点。 在这两种情况下,代理都会丢弃任何现有状态。 如果使用自动同步,那么主服务器必须以“让全世界等待”的方式将所有当前内容复制到新镜像。 在此操作期间,主机不接受任何读或写操作。 这种方法在大队列中会产生问题。

Kafka 是一种分布式日志,通常它比 RabbitMQ 队列存储更多的消息,而 RabbitMQ 队列中的数据在读取后就会从队列中删除。 活动队列应该保持相对较小。 但Kafka是一个日志,有自己的保留策略,可以设置几天或几周的时间段。 队列阻塞和全同步的方式对于分布式日志来说是绝对不能接受的。 相反,如果 Kafka 追随者的副本领先于领导者,他们只需将他们的日志截断到领导者的硬件(在他当选时)。 在更可能的情况下,当跟随者落后时,它只是开始从当前的 LEO 开始发出获取请求。

新的或重新加入的关注者在 ISR 之外开始,并且不参与提交。 他们只是与小组一起工作,尽快接收消息,直到追上领导者并进入情监侦。 没有锁定,也无需丢弃所有数据。

失去连接

Kafka 比 RabbitMQ 有更多的组件,因此当集群断开连接时它有一组更复杂的行为。 但Kafka最初是为集群设计的,所以解决方案是经过深思熟虑的。

以下是几种连接失败的场景:

  • 场景1:follower看不到leader,但仍然看到Zookeeper。
  • 场景 2:领导者看不到任何追随者,但仍然看到 Zookeeper。
  • 场景3:follower看到leader,但看不到Zookeeper。
  • 场景 4:领导者看到追随者,但看不到 Zookeeper。
  • 场景 5:follower 与其他 Kafka 节点和 Zookeeper 完全分离。
  • 场景 6:领导者与其他 Kafka 节点和 Zookeeper 完全分开。
  • 场景 7:Kafka 控制节点看不到另一个 Kafka 节点。
  • 场景 8:Kafka 控制器看不到 Zookeeper。

每个场景都有自己的行为。

场景1:Follower看不到leader,但仍然看到Zookeeper

RabbitMQ 与 Kafka:容错性和高可用性
米。 22. 场景一:三个副本的ISR

连接故障使代理 3 与代理 1 和 2 分离,但不与 Zookeeper 分离。 Broker 3 无法再发送提取请求。 时间过去后 副本.延迟时间.最大.ms 它已从 ISR 中删除,并且不参与消息提交。 一旦连接恢复,它将恢复获取请求并在追上领导者时加入 ISR。 Zookeeper 将继续接收 ping 并假设代理还活着并且运行良好。

RabbitMQ 与 Kafka:容错性和高可用性
米。 23. 场景 1:如果在replica.lag.time.max.ms 间隔内没有收到来自代理的提取请求,则从ISR 中删除代理

没有像 RabbitMQ 那样的裂脑或节点暂停。 相反,减少了冗余。

场景2:Leader看不到任何follower,但仍然看到Zookeeper

RabbitMQ 与 Kafka:容错性和高可用性
米。 24. 场景 2. 领导者和两个追随者

网络连接故障将领导者与追随者分开,但代理仍然可以看到 Zookeeper。 与第一种情况一样,ISR 缩小,但这次仅针对领导者,因为所有追随者停止发送获取请求。 再次强调,没有逻辑上的划分。 相反,在连接恢复之前,新消息的冗余会丢失。 Zookeeper 继续接收 ping 并相信代理还活着并且运行良好。

RabbitMQ 与 Kafka:容错性和高可用性
米。 25. 情景 2. ISR 已缩减至仅领导者

场景 3. Follower 看到了 Leader,但看不到 Zookeeper

follower与Zookeeper分离,但与leader的broker不分离。 结果,追随者继续发出获取请求并成为 ISR 的成员。 Zookeeper 不再接收 ping 并注册代理崩溃,但由于它只是一个跟随者,因此恢复后不会产生任何后果。

RabbitMQ 与 Kafka:容错性和高可用性
米。 26.场景3:follower持续向leader发送fetch请求

场景 4. Leader 看到了 follower,但没有看到 Zookeeper

RabbitMQ 与 Kafka:容错性和高可用性
米。 27. 场景 4. 领导者和两个追随者

领导者与 Zookeeper 分离,但与具有追随者的经纪人不分离。

RabbitMQ 与 Kafka:容错性和高可用性
米。 28.场景4:Leader与Zookeeper隔离

一段时间后,Zookeeper 将注册代理故障并通知控制器。 他将从他的追随者中选出一位新的领导者。 然而,原来的领导者将继续认为自己是领导者,并将继续接受来自 确认=1。 追随者不再向他发送获取请求,因此他会认为这些请求已死亡并尝试将 ISR 缩小到自身。 但由于它没有与 Zookeeper 的连接,因此它无法执行此操作,并且届时将拒绝接受任何进一步的条目。

我的消息 确认=全部 将不会收到确认,因为 ISR 首先打开所有副本,并且消息不会到达它们。 当原来的领导者试图将它们从 ISR 中删除时,它将无法做到这一点,并且将根本停止接受任何消息。

客户端很快就会注意到领导者的变化并开始向新服务器发送记录。 一旦网络恢复,原来的领导者发现自己不再是领导者,并将其日志截断为新领导者故障时的 HW 值,以避免日志发散。 然后它将开始向新的领导者发送获取请求。 原始领导者中未复制到新领导者的所有记录都会丢失。 也就是说,在两个领导者工作的那几秒钟内没有被原始领导者确认的消息将会丢失。

RabbitMQ 与 Kafka:容错性和高可用性
米。 29. 场景 4. 网络恢复后,broker 1 上的领导者成为跟随者

场景 5:follower 与其他 Kafka 节点和 Zookeeper 完全分离

follower 与其他 Kafka 节点和 Zookeeper 完全隔离。 他只是将自己从 ISR 中移除,直到网络恢复,然后追上其他人。

RabbitMQ 与 Kafka:容错性和高可用性
米。 30. 场景 5:从 ISR 中删除孤立的跟随者

场景 6:领导者与其他 Kafka 节点和 Zookeeper 完全分开

RabbitMQ 与 Kafka:容错性和高可用性
米。 31. 场景 6. 领导者和两个追随者

领导者与他的追随者、控制器和动物园管理员完全隔离。 在短时间内,它将继续接受来自 确认=1.

RabbitMQ 与 Kafka:容错性和高可用性
米。 32.场景6:将leader与其他Kafka和Zookeeper节点隔离

过期后未收到请求 副本.延迟时间.最大.ms,它会尝试将 ISR 缩小到自身,但由于没有与 Zookeeper 通信而无法这样做,然后它将停止接受写入。

同时,Zookeeper 将把孤立的 Broker 标记为死亡,控制器将选举一个新的领导者。

RabbitMQ 与 Kafka:容错性和高可用性
米。 33. 场景 6. 两位领导者

原始领导者可能会接受几秒钟的条目,但随后会停止接受任何消息。 客户端每 60 秒更新一次最新元数据。 他们将被告知领导者变更,并将开始向新领导者发送条目。

RabbitMQ 与 Kafka:容错性和高可用性
米。 34. 情景 6:制造商更换新领导者

自失去连接以来由原始领导者所做的所有已确认条目都将丢失。 一旦网络恢复,原来的leader会通过Zookeeper发现自己不再是leader了。 然后它会在选举时将其日志截断到新领导者的硬件上,并开始作为追随者发送请求。

RabbitMQ 与 Kafka:容错性和高可用性
米。 35. 场景6:网络连接恢复后,原来的leader变成了follower

在这种情况下,逻辑分离可能会在短时间内发生,但前提是 确认=1 и 最小同步副本数 另外 1. 逻辑分离会在网络恢复后,当原始领导者意识到他不再是领导者时,或者当所有客户端意识到领导者已更改并开始写入新领导者时自动结束 - 以先发生者为准。 无论如何,一些消息都会丢失,但仅限于 确认=1.

这种情况还有另一种变体,即在网络分裂之前,追随者落后了,领导者将 ISR 压缩到只有他自己。 然后由于连接丢失而变得孤立。 新的领导者当选,但原来的领导者继续接受条目,即使 确认=全部,因为 ISR 里除了他就没有其他人了。 一旦网络恢复,这些记录就会丢失。 避免此选项的唯一方法是 最小同步副本数 = 2.

场景 7:Kafka 控制器节点看不到另一个 Kafka 节点

一般来说,一旦与 Kafka 节点的连接丢失,控制器将无法向其传输任何领导者变更信息。 在最坏的情况下,这将导致短期逻辑分离,如场景 6 所示。通常,如果后者失败,经纪人将根本不会成为领导候选人。

场景 8:Kafka 控制器看不到 Zookeeper

Zookeeper 将不会收到来自失效控制器的 ping,并将选择一个新的 Kafka 节点作为控制器。 原始控制器可以继续这样呈现自己,但它不会收到来自 Zookeeper 的通知,因此它不会执行任何任务。 一旦网络恢复,他就会意识到自己不再是一个控制器,而是成为了一个普通的Kafka节点。

情景得出的结论

我们看到追随者连接的丢失不会导致消息丢失,而只是暂时减少冗余,直到网络恢复。 当然,如果一个或多个节点丢失,这可能会导致数据丢失。

如果领导者由于连接丢失而与 Zookeeper 分离,这可能会导致消息丢失 确认=1。 与 Zookeeper 缺乏沟通导致与两位领导人短暂的逻辑分歧。 这个问题通过参数解决 确认=全部.

参数 最小同步副本数 分成两个或多个副本提供了额外的保证,即这种短期场景不会导致像场景 6 中那样丢失消息。

丢失消息摘要

让我们列出在 Kafka 中丢失数据的所有方式:

  • 如果使用确认消息,则任何领导者失败 确认=1
  • 任何不干净的领导层过渡,即向情监侦之外的追随者过渡,即使有 确认=全部
  • 如果使用以下命令确认消息,则将领导者与 Zookeeper 隔离 确认=1
  • 已经将情监侦小组缩小到自己一人的领导人被完全孤立。 所有消息都会丢失,甚至 确认=全部。 这仅在以下情况下成立: min.insync.replicas=1.
  • 所有分区节点同时故障。 由于消息是从内存中确认的,因此某些消息可能尚未写入磁盘。 重新启动服务器后,某些消息可能会丢失。

可以通过禁止或确保至少两次裁员来避免不纯粹的领导层换届。 最耐用的配置是组合 确认=全部 и 最小同步副本数 更多1。

RabbitMQ和Kafka的可靠性直接对比

为了确保可靠性和高可用性,两个平台都实现了主复制系统和辅助复制系统。 然而,RabbitMQ 有一个致命弱点。 当发生故障后重新连接时,节点会丢弃其数据并且同步会被阻止。 这种双重打击让人对 RabbitMQ 中大型队列的寿命产生疑问。 您将不得不接受减少冗余或较长的阻塞时间。 减少冗余会增加大量数据丢失的风险。 但如果队列很小,那么为了冗余,可以使用重复的连接尝试来处理短时间的不可用(几秒钟)。

卡夫卡没有这个问题。 它仅丢弃领导者和追随者之间分歧点的数据。 所有共享数据均已保存。 此外,复制不会阻塞系统。 当新的追随者赶上时,领导者继续接受帖子,因此对于 DevOps 来说,加入或重新加入集群成为一项微不足道的任务。 当然,复制过程中还存在网络带宽等问题。 如果您同时添加多个关注者,您可能会遇到带宽限制。

当集群中的多台服务器同时故障时,RabbitMQ 的可靠性优于 Kafka。 正如我们已经说过的,RabbitMQ 仅在消息被主服务器和所有镜像写入磁盘后才向发布者发送确认。 但这会增加额外的延迟,原因有两个:

  • 每几百毫秒一次 fsync
  • 只有在检查每个节点可用性的数据包(网络滴答)的生命周期到期后,才能注意到镜像的故障。 如果镜子减慢或掉落,就会增加延迟。

Kafka 的赌注是,如果一条消息存储在多个节点上,那么它可以在消息到达内存后立即对其进行确认。 因此,存在丢失任何类型消息的风险(甚至 确认=全部, min.insync.replicas=2)在同时发生故障的情况下。

总体而言,Kafka 表现出了更好的软件性能,并且是专为集群而设计的。 如果需要可靠性,追随者的数量可以增加到 11 个。 复制因子 5 和同步的最小副本数 min.insync.replicas=3 将使消息丢失成为非常罕见的事件。 如果您的基础设施可以支持此复制比率和冗余级别,那么您可以选择此选项。

RabbitMQ 集群适用于小型队列。 但当交通繁忙时,即使是很小的队列也会迅速增长。 一旦队列变得很大,您就必须在可用性和可靠性之间做出艰难的选择。 RabbitMQ 集群最适合非典型情况,在这种情况下,RabbitMQ 的灵活性优势超过了集群的任何劣势。

针对 RabbitMQ 对大型队列的脆弱性,一种解决方法是将它们分成许多较小的队列。 如果您不需要对整个队列进行完整排序,而只需要相关消息(例如来自特定客户端的消息),或者根本不排序任何内容,那么此选项是可以接受的:查看我的项目 再平衡器 拆分队列(该项目仍处于早期阶段)。

最后,不要忘记 RabbitMQ 和 Kafka 的集群和复制机制中的一些错误。 随着时间的推移,系统变得更加成熟和稳定,但没有任何消息可以 100% 不会丢失! 此外,数据中心还发生大规模事故!

如果我遗漏了某些内容、犯了错误,或者您不同意其中的任何观点,请随时发表评论或与我联系。

经常有人问我:“Kafka 还是 RabbitMQ 选择什么?”、“哪个平台更好?”。 事实是,这实际上取决于您的情况、当前的经验等。我不太愿意发表自己的意见,因为为所有用例和可能的限制推荐一个平台过于简单化。 我写了这一系列文章,以便您可以形成自己的观点。

我想说,这两个系统都是这个领域的领导者。 我可能有点偏见,因为根据我的项目经验,我倾向于重视诸如保证消息排序和可靠性之类的东西。

我看到其他技术缺乏这种可靠性和有保证的排序,然后我研究了 RabbitMQ 和 Kafka,并意识到这两个系统令人难以置信的价值。

来源: habr.com

添加评论