zookeeper 功能简单,但是原理非常复杂,内部用 paxos 算法实现数据同步。
安装
1 | $ cp conf/zoo_example.cfg conf/zoo.cfg #里面的 tickTime=2000 表示2秒检查一次,分布式锁可以以此作为过期时间 |
命名服务
1 | create /foo bar #创建数据 |
可以看到 create -s /foo 的时候实际创建的是 /foo0000000001。foo后面跟的数字是有序的,可以作为集群获得唯一有序名称的来源。
注册中心
注册中心的需求是当集群里面有新节点进入或老节点退出的时候,其他节点能得到通知并更新数据。可以使用 zookeeper 的临时数据和观察特定来做到。
create -e 命令创建一个临时数据,当创建者断开连接的时候,数据被清除。利用的是心跳机制。 ls -w 观察一个节点
客户端1
1 | create /namespace |
客户端2
1 | ls -w /namespace |
这个时候删除 /namespace/node1 或者直接退出客户端1
客户端2
1 | WATCHER:: |
我们发现当客户端1退出的时候,客户端2收到了通知。这个时候客户端2只要重新从zookeeper拉取一次数据,就得到了最新的数据 [延伸阅读1]
分布式锁/集群选主
当一个客户端创建了 /path 数据后,别的客户端就不能创建了。利用这个特性可以实现分布式锁。
如果有一个集群需要一个主节点,这个主节点退出后其他节点得到通知,创建一个临时数据作为锁,申请到的节点就自封为主节点。如果这个主节点退出,那么这个锁会一起被清除。不影响其他节点申请锁。
zookeeper 选举原理
paxos 算法
paxos 算法是选举算法的一种,原理是客户端向一个节点写入数据的时候,节点把请求发给其他所有节点,如果得到了半数以上节点同意的回复,返回成功。这里同意的回复表示该节点的数据版本低于更新数据的版本。反对则表示该节点的数据也被另一个客户端改过了,版本号相同或更高。
fast-paxos 算法
paxos 允许所有节点可写,但是每次写数据都要经过选举,太慢。fast-paxos 算法给系统中选举出一个主节点,只有这个主节点可写,其他节点收到的写请求都被转发到这个主节点。
这种单写的优点是选出主节点后就不需要选举了,而且所有数据是有序的。缺点是单写有性能瓶颈。
主节点选举,选举阶段 Leader election
zookeeper 的主节点退出后其他节点会拒绝所有客户端写请求,进入选举状态。每个节点有一个XZID表示本地最新的事务编号
- 所有节点处于Looking状态,各自依次发起投票,投票包含自己的服务器ID和最新事务ID(ZXID)。
- 如果发现别人的ZXID比自己大,也就是数据比自己新,那么就重新发起投票,投票给目前已知最大的ZXID所属节点。
- 每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准Leader,状态变为Leading。其他节点的状态变为Following。
发现阶段 Discovery
- 为了防止某些意外情况,比如因网络原因在上一阶段产生多个Leader的情况。[延伸阅读2]
- Leader集思广益,接收所有Follower发来各自的最新epoch值。Leader从中选出最大的epoch,基于此值加1,生成新的epoch分发给各个Follower。
- 各个Follower收到全新的epoch后,返回ACK给Leader,带上各自最大的ZXID和历史事务日志。Leader选出最大的ZXID,并更新自身历史日志。
同步阶段 Synchronization
Leader刚才收集得到的最新历史事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才能成为正式的Leader。
延伸阅读
- 为什么 Apollo 没有使用 zookeeper?
Apollo使用的是 Eureka。因为 zookeeper 获得通知后要再次订阅,才能再次获得通知。
- 如果发生脑裂怎么办
脑裂指的是系统彻底分成两个部分,两个部分根据选举都选出了主节点。zookeeper规定服务器必须过半,即数量不到一半的那个网络,会停止服务。
参考:
https://juejin.im/post/5cdbda12e51d453ccd24650f
https://blog.csdn.net/u013256816/article/details/80865540