> 文章列表 > 手写分布式系统CP协议-实现中

手写分布式系统CP协议-实现中

手写分布式系统CP协议-实现中

按学习的进度安排,从深到广后重新写深入的项目》》》 终于到这一步了。

项目意义

为了秀肌肉

CP协议

CP二词来自CAP理论的CP二词,是在分布式系统中的核心概念【一致性】和【分区容错性】,在分布式系统中有不可提代的需求地位,包括目前流行的分布式:

  1. kafka
  2. k8s
  3. nacos

其中都有CP协议的实现,其意义和用途都非常的重要;

不懂P协议的必要性的读者可能得先深入了解分布式系统的实现

现状

当前的各项目CP协议实现都是高度定制、融合进项目,采用起来不方便

意义

  1. 秀肌肉&学习&铺路
  2. 分布式基础的深入了解
  3. 实现更高的性能

调研

主流协议

Paxos算法

著名的zookeeper采用的算法实现,十分的复杂,后多数采用Raft实现,本文不做调研

Raft算法

Raft是一种分布式一致性算法,用于在分布式系统中实现数据的一致性。Raft算法由Stanford大学的Diego Ongaro和John Ousterhout于2013年提出,相对于Paxos算法来说更易于理解和实现。
当下许多分布式系统采用该协议包括但不限于以下:

  1. Kafka
  2. K8s
  3. Nacos

本文的核心调研项目主要在Kafka和Nacos的实现中做调研。

无法直接告知读者CP协议的意义与用途,需要自己通过积累,去了解分布式中间件&集群的实现&理论知识
比如:mysql集群的实现和缺点、集群中心、分布式锁的意义、配置中心的系统架构与实现等等的分布式知识积累;
一个动画模拟网站 http://thesecretlivesofdata.com/raft/

Raft算法核心

所谓算法其实就是给定一个问题得到一个优秀的答案,
问题:分布式系统中(在多台机子)怎么实现数据的强一致性
解:按Raft定义的操作实现:

选举-核心、一

  1. 节点可以处于 3 种状态之一 : Follower、Candidate、Leader
  2. 一开始都是以Follower存在开始
  3. 每个节点都应该有一个明确的Leader,是自己或其他节点
  4. 如果Follower没有明确的Leader,那么他们可以成为Candidate
  5. Candidate会向其他节点发起投票,请求其他节点投选自己为Leader
  6. 接收到多数节点的【投票】回馈,那发起投票的Candidate就可以成为Leader
  7. 系统的所有更改都在Leader节点上完成

过程被称为Leader Election(选举)

选举-核心、二

现在要解释的是发生了一个更改的流程

每个更改都作为一个条目添加到节点的日志中 -> 一开始日志条目当前未提交,因此不会更新节点的值
-> 要提交条目,节点首先将其复制到跟随者节点 -> 然后领导者等待,直到大多数节点都写入了条目
-> 该条目现在已在领导节点上提交 -> 然后领导者通知追随者该条目已提交

  1. 集群现在已经就系统状态达成共识
  2. 这个过程称为日志复制

这个过程会有一次的通知准备,确认后,通知全体提交

建议看图解http://thesecretlivesofdata.com/raft/

手写分布式系统CP协议-实现中

选举-核心、三

单纯的实现以上其实不难,但需要考虑到很多细节,一下是对选举过程的深入;

在 Raft 中有两个超时设置来控制选举

选举超时

选举超时是追随者等待成为候选人的时间,选举超时随机在 150 毫秒到 300 毫秒之间。

也就是说:假如有三个集群节点,按【核心一】的描述,一开始它们都没有leader,如果同时成为的Condidate,那会造成冲突(冲突发起也没关系,可以通过代码逻辑去解决),使用随机的倒计时,倒计时决定Follower什么时候成为iCondidate,避免节点之间的Condidate冲突

  1. 选举超时后,跟Follower为Condidate并开始新的选举任期
  2. 并向其他节点发送请求投票消息,走【核心一】的流程
  3. 如果接收节点在这个任期内还没有投票,那么它将投票给候选人并且节点重置其选举超时
  4. 一旦候选人获得多数选票,它就成为领导者
  5. 领导者开始向其追随者发送附加条目消息
  6. 这些消息按心跳超时指定的时间间隔发送
  7. 追随者然后响应每个附加条目消息
  8. 这个选举任期将一直持续到跟随者停止接收心跳并成为候选人为止

【重置其选举超时】因为此时所有节点之间还没有共同的leader,所以在回票以后只重置选举超时时间
【附加条目消息】相当于告诉其他节点,我是Leader你重置【选举超时时间】,一种心跳保活机制
【心跳超时指定的时间】 在此之间是【选举超时时间】,在此之后的倒计时是统一的心跳过去时间

手写分布式系统CP协议-实现中

选举-核心、四

在Leader节点挂了后呢?

重新选举

在Leader节点出现意外

  1. 停止向Folllower发送【附加条目消息
  2. 此时【心跳倒计时】结束
  3. 重新开始随机的【选举超时】倒计时
  4. 重新开始【核心一】和【核心三】的流程

这次的选举后任期【Term】也变成了2,出现了第二任期的Leader

重复的Condidate

如果两个节点同时成为候选者,则可能会发生分裂投票。

随机的【选举超时】就是为了尽量避免

  1. 两个节点都开始了同一任期的选举
  2. 并且每个都先于另一个到达一个跟随者节点
  3. 现在每个候选人有 2 票,并且不能再获得这个任期
  4. 结束本期任期倒计时,节点重新随机【选举超时】

手写分布式系统CP协议-实现中

日志-核心、五

一旦我们选出了领导者,我们需要将对我们系统的所有更改复制到所有节点。

通过使用用于心跳的相同附加条目消息来完成的

  1. 首先,客户向领导者发送更改
  2. 更改附加到领导者的日志中,然后在下一次心跳时将更改发送给关注者
  3. 一旦大多数追随者承认,条目就会被提交,并向客户端发送响应

手写分布式系统CP协议-实现中

核心、六

Raft 甚至可以在网络分区时保持一致。

  1. 出现了网络分区,即将导致有两位不同任期的领导人
  2. 如果无法复制到多数(<1),因此它的日志条目保持未提交状态。
  3. 看到更高的选举任期并下台
  4. 回滚它们未提交的条目并匹配新领导者的日志

手写分布式系统CP协议-实现中

小结

简单了解后主要流程,可视的问题还有一些如下:

  1. 同步时等待下一次心跳携带日志发送,实时性不强
  2. 出现网络分区时,如果出现三个网络分区,其中两个分区的任期一致,在修复后怎么选择Leader

JRaft的实现

功能特性

  • Leader 选举和基于优先级的半确定性 Leader 选举
  • 日志复制和恢复
  • 只读成员(学习者角色)
  • 快照和日志压缩
  • 集群线上配置变更,增加节点、删除节点、替换节点等
  • 主动变更 Leader,用于重启维护,Leader 负载平衡等
  • 对称网络分区容忍性
  • 非对称网络分区容忍性
  • 容错性,少数派故障,不影响系统整体可用性
  • 多数派故障时手动恢复集群可用
  • 高效的线性一致读,ReadIndex/LeaseRead
  • 流水线复制
  • 内置了基于 Metrics 类库的性能指标统计,有丰富的性能统计指标
  • 通过了 Jepsen 一致性验证测试
  • SOFAJRaft 中包含了一个嵌入式的分布式 KV 存储实现

1

  • 多种日志存储实现: Berkeley DB、Hybrid、RocksDB(默认)、Logit
  • SofaRpc: 金融级稳定框架

2

  • 多种场景节点通信、用户端等都是用Netty对于场景特化不够好,性能损耗高
  • 协程模型

Kafka的实现

网络模型

Kafka的网络模型是基于TCP协议的,采用了一种基于事件驱动的非阻塞I/O模型,称为NIO(New I/O)。Kafka的网络模型主要包括以下几个组件:

  1. Broker:Kafka的服务器节点,负责接收和处理客户端的请求。
  2. Producer:生产者,向Kafka集群发送消息。
  3. Consumer:消费者,从Kafka集群中消费消息。
  4. Topic:消息主题,Kafka中的消息按照主题进行分类。
  5. Partition:分区,每个主题可以分为多个分区,每个分区可以在不同的Broker上进行副本复制。
  6. Replica:副本,每个分区可以有多个副本,用于提高数据的可靠性和可用性。
  7. Controller:控制器,负责管理Kafka集群的状态和分区的分配。

Kafka的网络模型采用了多线程的方式处理客户端请求,每个线程都维护一个Selector对象,用于监听多个Channel的事件。当有事件发生时,Selector会通知相应的线程进行处理。Kafka的网络模型还采用了零拷贝技术,避免了数据的多次复制,提高了性能和吞吐量。

一套代码看下来,他的selector适合创建多个,并且在每个特定的场景下共用,基本上每个selector都是单线程下,然后获取读取出来的请求体再进行业务操作

开源实现的优缺点

新设计

相比开源项目的优化点

  1. 协程
  2. 原生Nio
  3. Zore-Copy