我们基于XXL-JOB的架构原理,重新架构设计了支持多租户横向扩展的分布式任务调度平台。本篇介绍如何实现多个逻辑集群(多个租户逻辑上是独立的集群)的均衡选主。
我们用过的很多中间件都有实现多分片自动均衡选主的功能。
如分布式数据库TiDB,其存储引擎TiKV基于Raft算法实现分布式数据一致性,TiKV可以看成是一个大型Map,通过MulitRaft将这个大的Map分成多个Region,每个Region使用一个Raft Group实现数据一致性。
因为一个Raft集群只能有Leader节点负责读写请求,也就是说,一个有N个节点的集群,也只有一个节点是负责工作的,其它节点只是备用节点,这显然是资源浪费,也无法通过横向扩展集群性能。因此TiKV使用Multi Raft支持集群横向扩展。
多逻辑集群是指将一个物理集群分成多个逻辑集群,每个逻辑集群只能有Leader负责工作。均衡选主则是实现将多个逻辑集群的Leader节点均衡分布在每个物理节点上,将集群扩容或缩容时,也能重均衡分布这些逻辑集群的Leader节点。
多个逻辑集群容易实现,难的是如何实现多逻辑Leader节点在物理节点上均衡分布。本篇分享的是笔者想到的一种比较简单方案。
在物理节点固定的情况下,我们可以控制多个逻辑集群按顺序完成选举,这样就能控制每次选举哪个节点可以成为Leader节点。但如果是动态添加物理节点,或者某个物理节点挂了,就很难做到将一部分逻辑集群的Leader节点转移到新的节点,或是将挂掉的部分逻辑集群的Leader节点平均分配到其它物理节点。
所以笔者想了另一种方案,将逻辑集群分为选举集群和业务集群,选举集群有且仅有一个,业务集群可以有n多个。在开始业务集群的选举之前,先完成选举集群的Leader节点选举。其它业务集群的选举则由选举集群的Leader节点负责。
选举集群也需要实现数据一致性,当选举集群的Leader节点挂掉时,其它选举集群的节点成为新的Leader时才能正确的知道当前所有业务集群的选举情况。
我们以使用zookeeper实现多逻辑集群均衡选主为例。
首先基于zookeeper实现选主功能,为选举集群选出一个Leader节点。
当业务集群启动选主时,先往/namespace/${cluster}/nodes路径注册业务集群节点的ip和端口信息。
选举Leader节点监听/namespace路径的子节点变化,为${cluster}业务集群选主,往/namespace/${cluster}/leader路径注册该业务集群的Leader节点,该业务集群的其它节点监听路径:/namespace/${cluster}/leader,监听到节点新增事件则为选主完成,并继续监听该路径,以便监听Leader变更。
当某些业务集群的Leader节点所在的物理节点挂掉时,选举集群的Leader节点将负责重新选主那些以挂掉的物理节点为业务集群Leader节点的业务集群。
当选举集群的Leader节点所在的物理节点挂掉时,新的选举集群的Leader节点将接替选举工作。
当新增物理节点时,选举集群的Leader节点将寻找一些业务集群,将这些业务集群的Leader节点转移到新增的物理节点上。
使用Zookeeper实现较为麻烦,如果是基于Raft实现,那将会简单很多。推荐开源的Raft算法实现SDK:SOFARaft。我们之所以选择zookeeper,有其它的原因。
思考题:为什么Redis Cluster使用的是分槽算法,而不是像采用Raft等分布式数据一致性算法实现?
笔者的观点:Redis实现的是缓存功能,追求接近本地内存的读写性能,而Raft等分布式数据一致性算法都要实现数据复制到多数节点,这里会有性能开销,因此分槽比多逻辑集群能带来更好的性能。