文章摘要:BC-MQ 是中国移动苏州研发中心结合自身在云计算产品和技术的较多积累、自主研发的大云消息队列中间件产品,本文详细解读了 SOFAJRaft 在其消息云服务中的最佳应用实践。git
高可用的定义,指的是“一个系统通过特有的设计与改造,减小因不肯定故障停服的时间,从而对业务使用方来讲能够保证其服务的高度可用性”。在生产环境中,每每会存在不少不可预知的故障因素,好比虚拟机宕机、磁盘损坏和网络故障等,所以系统自身的高可用是任何工业级产品所需重点考虑的因素。github
对于消息队列服务来讲,考虑到故障切换和业务感知等问题,传统的高可用方式(冷备或者热备)通常都不太适用。在通过多种技术方案对比后,咱们发现采用基于 Raft 共识算法的多副本设计方案能够知足咱们产品的要求,所以在鉴权认证组件和API计量服务组件中,咱们集成了蚂蚁金服开源的 SOFAJRaft 库,实现这两个组件应对单点故障的高可用。算法
GitHub 地址:github.com/sofastack/s…编程
Raft 是一种分布式系统中易于理解的共识算法,该协议本质上是 Paxos 算法的精简版,而不一样的是依靠 Raft 模块化的拆分以及更加简化的设计,其实现起来更加容易和方便。[1]缓存
模块化的拆分主要体如今 Raft 把一致性协议划分为以下几部分:安全
而更加简化的设计则体如今:Raft 不容许相似 Paxos 中的乱序提交、简化系统中的角色状态(算法定义 Leader、Follower 和 Candidate 三种角色)、限制仅 Leader 可写入、采用随机超时触发 Leader Election 机制来避免“瓜分选票”等等。[2]性能优化
从上面的 Raft 算法总体结构图中能够看出,整个分布式系统中同一时刻有且仅有一个 Leader 角色的节点(如图最右边的服务器),只有 Leader 节点能够接受 Client 发送过来的请求。Leader 节点负责主动与全部 Follower 节点进行网络通讯(如图左边两个服务器),负责将本地的日志发送给全部 Follower 节点,并收集分布式系统中多数派的 Follower 节点的响应。此外,Leader 节点,还需向全部 Follower 节点主动发送心跳维持领导地位(即:保持存在感)。服务器
因此,只要各个节点上的日志保持内容和顺序是一致的,那么节点上的状态机就能以相同的顺序执行相同的命令,这样它们执行的结果也都是同样的。网络
目前,Raft 算法已经成熟地应用于诸多知名的开源项目中。业界很是著名的 Etcd(Kubernetes 高可用强一致性的服务发现组件)和 TiKV(高性能开源 KV 存储)均是 Raft 算法的实现。架构
为知足企业上云和构建万物相连的物联网业务需求,中国移动苏州研发中心结合自身在云计算产品和技术的较多积累,研发了大云消息队列中间件产品 BC-MQ。该产品基于 Apache 开源社区的 RocketMQ 内核,同时结合云端 PAAS 产品架构和消息中间件的应用业务需求进行深度优化和定制化的研发,提供了一款能够知足于云端场景的高性能、高可靠、低延迟和高可用的工业级产品。
本节从解决原有高可用技术方案的问题视角出发,同时结合选型 SOFAJRaft 库的原因,将详细阐述 BC-MQ 产品中的安全认证和 API 计量采集服务的高可用设计方案(注:这里不会涉及到安全认证和 API 计量采集组件自己的技术方案细节)。
在BC-MQ原有的方案中,多组安全认证服务各自独立部署组建集群,各个安全认证服务相互独立,没有主从关联,服务自己无状态,可水平任意扩展。安全认证服务的高可用依赖于RPC通讯的客户端保证,其主要经过负载均衡算法从安全认证服务集群选择一个节点发送RPC请求来实现租户级鉴权认证元数据的获取。在生产环境中,若是出现其中一个安全认证节点宕机不可用时,客户端的RPC通讯层可以及时感知并从本地的Node列表中剔除不可用节点。
集群中有状态的租户级安全认证元数据的强一致性由GlusterFS分布式文件存储的同步机制来保证。安全认证服务组建高可用集群的具体设计方案图以下所示:
而 BC-MQ 中 API 计量采集服务组件的高可用性则是依靠 Keepalived 组件的冷备模式结合 GlusterFS 分布式文件存储的同步机制共同保证,从而在必定程度上解决了 API 计量采集服务的单点不可用问题。API 计量采集服务的具体高可用设计方案图以下所示:
初步看上面的这种高可用技术方案挺完美的。可是通过验证和仔细推敲后就发如今生产环境中可能会存在以下几个问题:
因为“GlusterFS+Keepalived”的高可用方案存在上一节阐述的两个问题,因此咱们考虑是否能够采用其余的高可用方案来解决这两个问题?目标:即便生产环境出现部分节点故障后,安全认证和 API 计量组件依旧可以正常提供服务,作到业务无感知。
为了实现当分布式集群中的部分节点出现故障停服后,集群仍然可以自动选主继续正常对外提供服务,使得故障对外部业务不会产生任何影响,同时高可用方案又不能依赖外部系统,那咱们也就想到了 Raft 算法。Raft 算法设计,简洁易懂,没有任何外部依赖,能够完成一个高可靠、高可用、强一致的数据复制系统,解决咱们前面遇到的问题。
业界有一些 Raft 算法的实现,目前比较流行的主要有百度开源的Braft和蚂蚁金服开源的 SOFAJRaft。从官方 Github 上对两款开源 Raft 实现框架支持的功能和特性来看,基本相近,但 Braft 是 C/C++ 语言实现的,而 SOFAJRaft 是 JAVA 语言实现的,所以咱们从技术栈、集成难易和运维成本等角度综合考虑,最终选择了 SOFAJRaft。
SOFAJRaft 是一个基于 Raft 一致性算法的生产级高性能 JAVA 实现,支持 MULTI-RAFT-GROUP,适用于高负载低延迟的场景。使用 SOFAJRaft,使用者能够更加专一于本身的业务领域,由 SOFAJRaft 负责处理全部与 Raft 算法相关的技术难题,而且 SOFAJRaft 比较易于使用,用户能够经过 Github 上的几个示例在很短的时间内掌握并使用它。下面先简单介绍下 SOFAJRaft 的特性和加强功能点:
其中:
为了提供支持生产环境运行的高性能,SOFAJRaft 主要作了以下几部分的性能优化,其中:
所以,综上所述咱们最终选用 SOFAJRaft 的理由以下:
BC-MQ在集成SOFAJRaft库后在部署架构、数据持久化和高可用模式上都进行了能力升级,较好地解决了“GlusterFS+Keepalived”中的问题。
组件服务端的状态机接口实现
针对具体的业务应用而言(对 BC-MQ 来讲,就是 API 计量统计和安全认证鉴权),状态机(StateMachine)是业务逻辑实现的主要接口,状态机运行在每一个Raft节点上,提交的 任务 Task 若是成功,最终都会复制应用到分布式集群中的每一个节点的状态机上。
在 SOFAJRaft 中提供了一个已经具有绝大部分默认实现的抽象适配类— StateMachineAdapter,直接继承它可使得业务应用避免实现全部的接口。咱们根据 BC-MQ 组件改造的需求,对部分接口作了以下的实现:
1.void onApply(Iterator iter):该方法是 SOFAJRaft 中最为核心的接口。在整个分布式集群环境中,待同步的数据会封装成 LogEntry 复制到其余节点。在数据同步完成以后,进程会提交到自身状态机的这个方法中执行。在 BC-MQ 中,API 计量采集服务在计量统计数据日志同步至 Follower 节点后,SOFAJRaft 在业务状态机的 onApply 方法中调用 API 计量采集服务组件的存储接口进行持久化。
2.void onLeaderStart(long term)/void onLeaderStop(Status status):这个两个方法是在节点经过选举成为 Leader 和失去 Leader 资格时调用,BC-MQ 的安全认证和 API 计量服务组件自己也维护了 Raft 的角色状态(这里的角色状态与 SOFAJRaft 自己的是保持一致的)。在节点的角色发生转变的时候,须要调用这个方法,将组件的角色和状态转变一致。这样实现主要是与 BC-MQ 的业务场景相关,在集群中通过从新选举后节点角色转变时,只有API 计量组件服务的 Leader 节点才可以执行消息队列的 API 计量采集相关的定时任务。
3.void onSnapshotSave(SnapshotWriter writer, Closure done)/boolean onSnapshotLoad(SnapshotReader reader):这两个方法是 SOFAJRaft 快照相关的接口调用,快照自己的做用就是在有新的节点加入到 SOFAJRaft Group 时,不须要加载所有的 Log 日志数据,而只须要从最近的 index 开始加载,这能够节省从 Leader 节点同步大量日志信息所形成的网络通讯开销。BC-MQ 的安全认证和 API 计量采集服务组件实现了这两个方法,用于实现快照的特性。
客户端请求重定向机制优化
SOFAJRaft 中默认只有 Leader 节点可以被客户端访问到,全部的日志提交都须要先提交到集群的 Leader 节点,而后由Leader节点同步到其余的 Follower 节点。BC-MQ 的安全认证服务和 API 计量服务组件经过 SOFAJRaft 改造后,在 BC-MQ 中原有的客户端 RPC 请求访问方式也须要通过一些优化设计,为了让客户端可以实时感知到分布式集群环境中当前的 Leader 节点,所以须要在客户端缓存一个集群的节点列表 NodeList 和 LeaderId。
仅仅在客户端维护一个本地缓存还不够,由于若是集群中的 Leader 节点出现了宕机的故障时,集群会发生从新选举,那么客户端缓存的 Leader 节点信息就会过时,这就须要客户端就可以感知到 Leader 节点的变化。为解决这个问题,咱们采用了 RPC 请求重定向机制来保证,一旦RPC请求发送到了集群中的 Follower 节点,那么 Follower 会将该请求重定向到 Leader。如下为 BC-MQ 客户端通讯重定向机制优化设计图:
下面展现的是 BC-MQ 的安全认证服务和 API 计量服务组件的部分测试用例,从用例的实际执行状况来看,与咱们的预期结果彻底一致能够知足生产环境高可用的业务场景。
序号 | 具体业务场景 | 预期结果 | 实际结果 |
---|---|---|---|
1 | 安全认证组件3节点部署,Kill掉其中1个节点,客户端持续发布/订阅带鉴权的消息 | 安全认证组件Leader角色转换,客户端发布/订阅带鉴权消息无任何影响 | 与预期一致 |
2 | 安全认证的5节点部署,Kill掉其中2个节点,客户端持续发布/订阅带鉴权的消息 | 安全认证组件Leader角色转换,客户端发布/订阅带鉴权消息无任何影响 | 与预期一致 |
3 | API计量组件3节点部署,Kill掉其1个节点,客户端持续;发布/订阅带鉴权的消息 | API计量组件Leader角色转换,输出的API计量文件正确 | 与预期一致 |
4 | API计量组件5节点部署,Kill掉其2个节点,客户端持续发布/订阅带鉴权的消息 | API计量组件Leader角色转换,输出的API计量文件正确 | 与预期一致 |
5 | 在集群中模拟出现网络分区(对称/非对称)的场景,安全认证服务集群是否会出现脑裂现象,鉴权认证数据是否正确 | 网络分区(对称/非对称)场景下,集群不会出现脑裂,而且鉴权数据是正确的 | 与预期一致 |
6 | 在集群中模拟出现网络分区(对称/非对称)的场景,API计量服务集群是否会出现脑裂现象,API计量数据是否正确 | 网络分区(对称/非对称)场景下,集群不会出现脑裂,而且API计量数据是正确的 | 与预期一致 |
7 | 在3节点组成的安全认证服务集群的负载工做的场景下,向该RaftGroup添加1/2节点,客户端持续发布/订阅带鉴权的消息 | 客户端发布/订阅带鉴权消息无任何影响 | 与预期一致 |
8 | 在5节点组成的安全认证服务集群的负载工做的场景下,移除该RaftGroup中的1/2节点,客户端持续发布/订阅带鉴权的消息 | 客户端发布/订阅带鉴权消息无任何影响 | 与预期一致 |
本文主要介绍了中国移动苏州研发中心自主研发的 BC-MQ 产品中两个重要组件—安全认证和 API 计量服务是如何经过集成开源的 SOFAJRaft 库解决原来“GlusterFS+Keepalived”高可用方案中遇到的问题,以实现大规模消息队列云服务集群的高可用部署优化方案。因为文章篇幅的缘由,本文没有对 BC-MQ 自己多项重要的特性进行详细介绍,做者将在后续的文章中继续进行阐述。同时,限于笔者的才疏学浅,对本文内容可能还有理解不到位的地方,若有阐述不合理之处还望留言一块儿探讨。
[1] Diego Ongaro and John Ousterhout.Raft Paper. 2013
[2] 详解蚂蚁金服 SOFAJRaft | 生产级高性能 Java 实现
做者介绍: 胡宗棠,中国移动苏州研发中心云计算中间件团队负责人,Apache RocketMQ Committer,Linux OpenMessaging Advisory Borad Member,SOFAJRaft Contributor,熟悉分布式消息中间件的设计原理、架构以及各类应用场景。
公众号:金融级分布式架构(Antfin_SOFA)