JRaft–基础–01–介绍
1、什么是JRaft?
2、JRaft 整体功能和性能优化
2.1、功能支持
2.1.1、Leader election
Leader 选举,就是raft 中的 leader 机制
2.1.2、Log replication and recovery
- 日志复制
- 保证已经被 commit 的数据一定不会丢失,即一定要成功复制到多数派
2.1.3、Log recovery
- 日志恢复,有2个类型
- Current term 日志恢复
- Prev term 日志恢复
- Current term 日志恢复:主要针对一些 follower 节点重启加入集群或者是新增 follower 节点后如何追日志
- Prev term 日志恢复:主要针对 leader 切换前后的日志一致性
2.1.4、Snapshot and log compaction
定时生成 snapshot,实现 log compaction 加速启动和恢复,以及 InstallSnapshot 给 followers 拷贝数据,如下图:
2.1.5、Membership change
用于集群线上配置变更,比如增加节点、删除节点、替换节点等
2.1.6、Transfer leader
主动变更 leader,用于重启维护,leader 负载平衡等
2.1.7、Symmetric network partition tolerance:对称网络分区容忍性
如上图 S1 为当前 leader,网络分区造成 S2 不断增加本地 term,为了避免网络恢复后 S2 发起选举导致正在工作的 leader step-down,从而导致整个集群重新发起选举,JRaft 中增加了 pre-vote 来避免这个问题的发生。
JRaft 中在 request-vote 之前会先进行 pre-vote(currentTerm + 1, lastLogIndex, lastLogTerm),多数派成功后才会转换状态为 candidate 发起真正的 request-vote,所以分区后的节点,pre-vote 不会成功,也就不会导致集群一段时间内无法正常提供服务
2.1.8、Asymmetric network partition tolerance:非对称网络分区容忍性
如上图 S1 为当前 leader,S2 不断超时触发选主,S3 提升 term 打断当前 lease,从而拒绝 leader 的更新。
在 JRaft 中增加了一个 tick 的检查,每个 follower 维护一个时间戳记录下收到 leader 上数据更新的时间(也包括心跳),只有超过 election timeout 之后才允许接受 request-vote 请求
2.1.9、Fault tolerance:容错性,少数派故障不影响系统整体可用性,包括但不限于:
- 机器掉电
- 强杀应用
- 慢节点(GC, OOM 等)
- 网络故障
- 其他各种奇葩原因导致 raft 节点无法正常工作
2.1.10、Workaround when quorate peers are dead(法定人数同行死亡时的临时措施)
多数派故障时,整个 grop 已不具备可用性,安全的做法是等待多数节点恢复,只有这样才能保证数据安全
但是如果业务更加追求系统可用性,可以放弃数据一致性的话,JRaft 提供了手动触发 reset_peers的指令以迅速重建整个集群,恢复集群可用
2.1.11、Metrics
JRaft 内置了基于 metrics 类库的性能指标统计,具有丰富的性能统计指标,利用这些指标数据可以帮助用户更容易找出系统性能瓶颈
2.1.12、Jepsen
除了几百个单元测试以及部分 chaos 测试之外,JRaft 还使用 jepsen 这个分布式验证和故障注入测试框架模拟了很多种情况,都已验证通过:
- 随机分区,一大一小两个网络分区
- 随机增加和移除节点
- 随机停止和启动节点
- 随机 kill -9 和启动节点
- 随机划分为两组,互通一个中间节点,模拟分区情况
- 随机划分为不同的 majority 分组
2.2、性能优化
除了功能上的完整性,JRaft 还做了很多性能方面的优化,这里有一份 KV 场景(get/put)的 benchmark 数据,在小数据包,读写比例为 9:1,保证线性一致读的场景下,三副本最高可以达到 40w+ 的 ops。
这里挑重点介绍几个优化点
2.2.1、Batch
我们知道互联网两大优化法宝便是 cache 和 batch,JRaft 在 batch 上花了较大心思,整个链路几乎都是 batch 的,依靠 disruptor 的 MPSC 模型批量消费,对整体性能有着极大的提升,包括但不限于:
- 批量提交 task
- 批量网络发送
- 本地 IO batch 写入
- 要保证日志不丢,一般每条 log entry 都要进行 fsync 同步刷盘,比较耗时,JRaft 中做了合并写入的优化
- 批量应用到状态机
- 需要说明的是,虽然 JRaft 中大量使用了 batch 技巧,但对单个请求的延时并无任何影响,JRaft 中不会对请求做延时的攒批处理
2.2.2、Replication pipeline(流水线复制)
通常 leader 跟 followers 节点的 log 同步是串行 batch 的方式,每个 batch 发送之后需要等待 batch 同步完成之后才能继续发送下一批(ping-pong),这样会导致较长的延迟。
JRaft 中通过 leader 跟 followers 节点之间的 pipeline 复制来改进,非常有效降低了数据同步的延迟,提高吞吐。经我们测试,开启 pipeline 可以将吞吐提升 30% 以上,详细数据请参照 benchmark
2.2.3、Append log in parallel
在 JRaft 中 leader 持久化 log entries 和向 followers 发送 log entries 是并行的
2.2.4、Fully concurrent replication
Leader 向所有 follwers 发送 log 也是完全相互独立和并发的
2.2.5、Asynchronous
JRaft 中整个链路几乎没有任何阻塞,完全异步的,是一个完全的 callback 编程模型
2.2.6、ReadIndex
优化 raft read 走 raft log 的性能问题,每次 read,仅记录 commitIndex,然后发送所有 peers heartbeat 来确认 leader 身份,如果 leader 身份确认成功,等到 appliedIndex >= commitIndex,就可以返回 client read 了,基于 ReadIndex follower 也可以很方便的提供线性一致读,不过 commitIndex 是需要从 leader 那里获取,多了一轮 RPC
2.2.7、Lease Read
JRaft 还支持通过租约(lease)保证 leader 的身份,从而省去了 ReadIndex 每次 heartbeat 确认 leader 身份,性能更好,但是通过时钟维护 lease 本身并不是绝对的安全(时钟漂移问题),所以 JRaft 中默认配置是 ReadIndex,因为通常情况下 ReadIndex 性能已足够好
3、JRaft 应用场景?
- Leader 选举
- 分布式锁服务,比如 zookeeper,在 JRaft 中的 RheaKV 模块提供了完整的分布式锁实现
- 高可靠的元信息管理,可直接基于 JRaft-RheaKV 存储
- 分布式存储系统,如分布式消息队列、分布式文件系统、分布式块系统等等