ZooKeeper是分布式一致性存储系统。ZooKeeper客户端可以利用ZooKeeepr的特性实现自身的选主。本文介绍一种可行的方法(主要思路):
需求:有一个ZooKeeper集群,若干ZooKeeper客户端,这些客户端需要利用ZooKeepr选出一个主,其它客户端都是从。
- 在ZooKeeper上创建出选主所需要的目录,例如/election,我们称之为选主目录。
- 客户端在选主目录下,创建一个临时顺序节点(EPHEMERAL_SEQUENTIAL),节点的内容可以为客户端本身的一些标识(这样其它客户端之间可以做服务发现和master发现)。
- 收集选主目录下的所有子节点(getChildren),排序,如果本客户端是第一个子节点,那么自身为主。否则,为从。作为从节点,可以监听选主目录,这样当主节点与ZooKeeper断开连接时(临时节点被删除)会得到通知,然后从节点就可以再次尝试选主。当然,ZooKeeper的watch有坑的,自己想办法避免吧。
当然,还有很多其它的方法,比如利用ZooKeeper做一个类似分布式锁的机制,谁抢到锁谁就是leader,具体不再细说了。
双主问题:
所有利用外部分布式共识服务来实现的选主,都不可避免在一定程度上遇到双主问题。如果短暂的双主对业务影响不大,那么是ok的。如果影响严重那么就要慎重考虑。以下方案可以解决双主问题:
- 上述客户端,不依赖外部服务(zk),而是自己用raft等共识协议实现选主,所有重要操作直接走共识协议,保证只有leader才可以最终写成功,只有被commit的操作才能最终影响集群。这是最安全的,也是性能最高的。
- 如果第1种方案不可行或者觉得工作量大,那么可以:
a. 所有重要操作都需要从外部服务申请『令牌』,申请过程是原子的,保证只有一个可以申请成功。拿到『令牌』的客户端,就可以做某种操作。在zk上,这个获取『令牌』的过程可以就是一次带version的更新。
b. 某种fence机制,保证新的主产生后,真正开始提供服务之前,旧的主无法再访问关键服务或者数据 。