开源一个千万级多组Raft库-Dragonboat
向大家介绍Dragonboat,一个开源的Go实现的多组Raft库,项目已Apache2协议下开源。欢迎大家试用,也请大家点star鼓励:https://github.com/lni/dragonboat
通俗的讲,这是一个分布式共识协议库,应用可以用它把数据分布存储于多台机器上,只要过半数的机器在线,数据与服务便可在线。这避免了因个别机器当机或网络故障而造成数据、服务不可用,提高系统可用性。它提供称为Linearizability的强一致特性:多个副本的数据在外部看来更像使用单一副本,不会有仅提供最终一致性的系统常见且难缠的读到旧版数据的问题。
基于Raft协议的共识库已经应用于很多互联网后台系统。接触了很多用户以后,普遍反馈的当前应用障碍是缺乏一个可靠、高性能、且对共识协议本身接近全透明的开箱即用的通用实现。
优势
Dragonboat已经较好的解决所有上述应用障碍:
- 测试最完备的开源Raft库之一,所有的实现代码、测试工具、测试结果均已开源。
- 吞吐性能最好的开源Raft库,千万级每秒实现,平均单核即达每秒40万次以上写吞吐。
- 功能最完备的开源Raft库,不做特殊应用假设限制,安全可靠前提下最大程度确保通用性。
同时,Dragonboat的Go实现经过大量具体性能优化打磨,在当前高性能Go系统在行业内需求持续剧增的背景下,为准备入坑的同学踩坑带路提供参考。
功能与使用
Dragonboat实现了Raft论文中提及的几乎所有功能,是当前最完备的Raft协议的Go实现:
- 选主与复制
- 快照
- 基于ReadIndex协议的高性能强一致读
- 主节点转移
- 无投票权节点
- 节点静默
- Quorum自查
- 对应用透明的幂等更新支持
- 全异步的执行与快照操作
Dragonboat的使用十分方便。与etcd Raft库不同,Dragonboat无需用户参与任何Raft协议状态有关的操作,最大程度降低Raft的使用成本与人为错误概率。用户首先实现一个IStateMachine接口用以描述更新与查询请求的执行方法,该接口仅四个方法必须实现,分别用于更新、查询StateMachine,以及对StateMachine创建与恢复快照。实际项目经验发现,一个简单的内存上的Key-Value数据库,它的StateMachine几分钟就能作出一个原型。
有了上述IStateMachine实例,便可根据应用需求使用Dragonboat的应用API接口提出各类请求,系统将严格通过Raft协议规定的要求处理各用户请求,并最终提交至用户的IStateMachine实例完成状态更新与查询的执行。
Dragonboat自带的详细中文例程可以在十几分钟那让用户了解整个使用流程,近距离观察共识协议给系统所带来的fault-tolerance容错特性,并实际操作体验如发起Proposal、强一致读、Raft组成员变更、节点重启等系统功能。
设计与实现概述
Dragonboat的核心组件是分布在网络各服务器上的NodeHost,通常每台服务器一个NodeHost实例,用以分配使用计算、存储与通讯资源,并管理运行于该服务器上的来自各不同Raft组的成员节点。
NodeHost同时提供一个facade interface,用以提供所支持的各服务,用户可使用其API完成各支持的功能,如发起读写或成员变更请求,启动或停止某成员节点,请求主节点转移或查询当前组成员等等。请使用上一段提供的例程的链接,具体了解NodeHost的使用。
各Raft成员节点内含下列实例:
- Raft协议状态
- Raft组成员
- 应用IStateMachine
- 用于支持幂等更新的Session状态
其中应用IStateMachine由用户提供实现,其余皆为系统实现并对用户完全透明。
为了原生支持大量Raft组,各类batching与pipelining优化手段被仔细的落实到每个细节,如驱动各Raft组更新执行的执行引擎就是最好例子。以写(propose)为例,如下图所示,各Raft组被分配到不同的执行shard上,以提供parallelism,每个shard又是一个多级流水线,不同处理阶段不同性质(IO密集、内存访问密集等)的处理在流水线不同级间并发完成,充分利用concurrency优势将所有消息传递、执行更新等操作异步化。
Dragonboat的Log存储组件称为LogDB,默认使用RocksDB,但可方便替换为其它Key-Value store方案。默认的RocksDB适配仅350行Go代码。NodeHost间消息传输由称为Raft RPC的组件完成,系统提供了默认的TCP与gRPC两个实现,它们均支持Mutual TLS,同时也可方便地适配其它传输方案。
测试与正确性检查
Dragonboat经过及其严格的测试。相对于用宣传式口号称赞软件如何可靠,下列具体数字和事实相信更具有说服力:
- 每晚千万次规模,历时一年多共百亿次规模的随机行为组合下的节点重启与网络partition恢复测试,发现并纠正了诸多包括etcd亦存在的Raft实现错误
- 数万行全手写高覆盖测试代码,Raft协议核心3000行代码便有高达万行测试代码护航
- 移植并通过了所有etcd raft相关测试代码,覆盖etcd累积的各类可能的小概率意外情况
- 系统的测试方法:单元与集成测试,Jepsen测试,fuzz随机输入测试,随机组合行为测试,I/O错误注入测试,文件系统掉电测试
- 全面的检查:linearizability检查,应用状态机状态一致、Raft组成员一致、Raft组可用、磁盘上Raft Log Entry一致
随机行为组合测试中,保存了部分Raft组的I/O历史数据,可通过Jepsen的Knossos工具进行系统的Linearizability检查,这些数据也已开源。
性能分析
在22核2.8GHz的三组服务器间,对16字节的负载,当使用48个Raft组,Dragonboat可以持续每秒900万次的写入,或在9:1的高读写比场景下持续进行每秒1100万次的混合读写操作。跨地域高延迟场景下,高吞吐依旧被保持。
活跃组的数量增加会因为batching变得更困难而直接降低吞吐,但吞吐始终是百万数量级的。大量的空闲组则不会显著影响吞吐。
下表是Dragonboat的写延迟数据。在每秒800万次16字节写的情况下,P99写延迟依旧小于5毫秒。得益于ReadIndex协议无需落盘写的特性,读延迟通常情况下显著小于写延迟。
Go的GC具有所有主流语言中最低的STW停顿,这对延迟及延迟离散度敏感场景及其重要。在每秒千万请求的压力下,STW停顿为400微秒。在Go 1.12中,该延迟将继续减半。下图是120个连续GC周期的所有STW停顿时长,该测试场景下每秒平均3个GC周期。
Dragonboat为多组Raft而优化,单Raft组性能尚未经任何优化。当前版本,单Raft组场景下可持续每秒125万次的16字节的写,平均延迟为1.3毫秒,P99延迟2.6毫秒。三组服务器共占用9个2.8GHz CPU核心,平均每台服务器占用3个2.8GHz的CPU核心。
{!-- PGC_COLUMN --}© 著作权归作者所有
作者:nilei
原文:https://my.oschina.net/u/4062427/blog/3001821