集群中的 server 分为三种角色:leader , follower , observer 。 
其中observer 是配置zoo.cfg 明确定义的,角色leader 在一个zookeeper集群中有且只能有一个,是通过内部的选举机制临时产生的。 leader 是集群中最重要的角色。负责响应集群的所有对Zookeeper数据状态变更的请求。它会将每个状态更新请求进行顺序管理,以便保证整个集群内部消息处理的 FIFO,遵循了顺序一致性(Sequential Consistency)。
leader 内部维护 session ,来自客户端的连接和断开连接,都会被统一follower 或 observer 转发给leader 处理。
leader 内部维护单调递增的 Zxid(ZooKeeper Transaction Id ),针对客户端连接,断开连接,节点的写操作都会分配一个全局唯一的Zxid,同时这些操作是原子性的,并且是严格顺序性的,遵循ZAB 原子广播一致性协议完成事务(transaction)操作。如果客户端的所有写操作,都会被 follower 统一转发给leader 处理。
follower 具有选举权。负责提供给客户端读写服务,需要响应leader 的提议
observer 没有选举权。主要提供给客户端读服务,不提供写服务,也不需要响应leader 的提议。也不需要日志文件,因为没有写服务,没有持久化的需要。
Server状态Zookeeper 的存储zookeeper中的znode数据都是在内存中优先维护和提供读服务,当事务被提交以及最终提交都会持久化到磁盘的日志文件中。 Zookeeper 的内部网络拓扑Zookeeper 在内部网络中如何实现两两连接的? 
这里暂且使用 10.0.2.30,10.0.2.31,10.0.2.32,10.0.2.33 替代 node1,node2,node3,node4,并依次启动 zookeeper。 server.1=10.0.2.30:2888:3888
server.2=10.0.2.31:2888:3888
server.3=10.0.2.32:2888:3888
server.4=10.0.2.33:2888:3888 依次使用 netstat 查看网络连接情况 
可以看出来 node1作为服务节点,由 node2,node3,node4 通过 3888端口建立连接进来。 node1 作为客户端连接 node3 (目前node3是leader ) 的2888端口。 
可以看出来 node2作为服务节点,由 node3,node4 通过 3888端口建立连接进来。 但是 node2 作为客户端连接node1的3888端口。 node2 作为客户端连接 node3 (目前node3是leader ) 的2888端口。 
可以看出来 node3作为服务节点,由 node4 通过 3888端口建立连接进来。 但是 node3 作为客户端连接node1,node2 的3888端口。 node3 作为leader 节点,由 node1,node2 ,node4 通过 3888端口建立连接进来。 至于为什么 node3能当选 leader 呢?可以在下面的 选举过程中 得到进一步详细的阐述。 
可以看出来 node4 作为客户端连接node1,node2 ,node3 的3888端口。 node4 作为客户端连接 node3 (目前node3是leader ) 的2888端口。 ZAB(原子广播,Zookeeper Atomic Broadcast )https://zookeeper./doc/current/zookeeperInternals.html ZAB(Zookeeper Atomic Broadcast )原子广播是 Paxos 分布式一致性协议算法(http://zh./zh-cn/Paxos) 的一个简化版本。 首先有以下概念,我们需要了解: 数据包(Packet ):通过 FIFO Channel 发送的字节数组。 提案(Proposal ):协议的单位。通过与ZooKeeper中的法定server交换数据包来达成协议。大多数提案都包含消息,但是 NEW_LEADER Proposal 提案是不包含消息。 消息(Message ):要自动广播到所有ZooKeeper服务器的字节数组。消息被包含在一个提案(Proposal )中,并且只有在提案(Proposal )被通过后消息才会最终交付delivered (提交到事务日志和更新内存的统一视图)。 法定人员 (Quorum ):有 Zookeeper集群中非observer 角色的所有服务器节点组成,具有投票通过提案(Proposal )的权力。
在 Zookeeper 中提供以下的保证数据的严格顺序: 传递可靠性:如果一个消息被一个server最终交付delivered ,那么这个消息最终也被其他所有的server最终交付delivered ,这里指最终一致性。 顺序全局性:如果一个消息a先于b被一个server最终交付delivered ,那么消息a也是先于b被其他所有的server最终交付delivered 。 顺序传递性:如果消息a先于b被发送到server,消息b先于c被发送到server,那么消息a也是先于c被server接收的。
如上所述,ZooKeeper保证消息的总顺序,也保证建议的总顺序。 ZooKeeper使用ZooKeeper transaction id (zxid) ,这是一个全局的唯一的ID。提出提案(Proposal )时,所有提案都将附上zxid,进而保证全局的顺序性。 其中确认acknowledge 提案(Proposal )表示服务器已将提案(Proposal )持久化到日志中。 稍微注意一下:法定人员(Quorum )收到提案(Proposal ),只存在确认(acknowledge ),或者因为网络等原因超时响应,不存在反对(reject )。 ZooKeeper消息传递包括两个阶段: leader选举leader 产生的条件:

当集群中任意具有选举权的server发现leader挂了: 该 server 会触发NEW_LEADER Proposal 提案,给自己投票,并通过 ZAB 广播给所有连接的 server。 接受到 NEW_LEADER Proposal 提案的server,如果有被选举权,则会触发它的投票行为: 最终满足leader 条件的server,将被选出,同时 follower 也被广播获得 Proposal 的提交。
以上中的 网络拓扑 为什么 node3能当选 leader 呢? node1 启动时,给自己投票,因为其他server尚没启动,因为 node1 依然在LOOKING 竞选状态。 node2 启动完,给自己投票,同时与 node1 交换了Zxid和myid,node2 胜出,但因为没有达到半数以上法定人员,所以node1,node2 依然处于LOOKING 竞选状态。 node3 启动完,给自己投票,同时与 node1 ,node2 交换了Zxid和myid,node3 胜出,也达到半数以上法定人员(3 > 4/2),因此 node3 被选举为 leader 。 node4 启动完,给自己投票,同时与 node1 ,node2 ,node3交换了Zxid和myid,node3的Zxid最新,因此 node4 追随 node3。
活动消息传递消息的传递一般指写请求。 
当 follower 接收到 客户端的 写请求后,会转发给 leader 顺序处理。 leader 收到写请求,会检查数据问题,如无问题,创建一个新的提案proposal 加入toBeApplied FIFO 队列,内容是写请求的消息,并附上全局的ZXid。
leader 每次toBeApplied FIFO 队列头部取到一个提案proposal ,通过 ZAB 广播给所有的 follower ,处于 pending 等待回复。
follower 收到提案proposal 后,记录提案proposal 持久化到磁盘的日志文件中,然后确认(acknowledge )回复这个提案(Proposal )给leader 。
leader 处于 pending 等待回复,一旦收到follower 加上自己的确认(acknowledge )超过半数法定人员(Quorum ),就会触发 Commit 阶段,发送commit 请求给所有的follower ,发送info 请求所有的observer 。 同时,leader 将提案proposal 放入 committedRequest 队列,并从toBeApplied FIFO 队列移出该 提案proposal 。
follower 收到 Commit 后,会更新自己的内存数据,统一数据视图。
observer 收到info 后,会更新自己的内存数据,统一数据视图。

针对客户端的读请求,则不需要转发给leader 处理。 当然如果是客户端的sync 命令,则会触发客户端连接的follower 或observer 向leader 请求同步数据状态。
|