Zookeeper是一个高性能分布式应用协调服务
Zookeeper工作方式
1. Zookeeper集群包含1个Leader,多个Follower。
2. 所有的Follower都可提供读服务
3. 所有的写操作都会被forward到Leader
4. Client与Server通过NIO通信。
5. 全局串行化所有的写操作
6. 保证同一客户端的指令被FIFO执行
7. 保证消息通知的FIFO
Zab协议 – 广播模式
1. Leader将所有更新(称为proposal),顺序发送给Follower
2. 当Leader收到半数以上的Follower对此proposal的ACK时,即向所有Follower发送 commit消息,并在本地commit该消息
3. Follower收到Proposal后即将该Proposal写入磁盘,写入成功即返回ACK给Leader
4. 每个Proposal都有一个唯一的单调递增的proposal ID,即zxid
Zab协议 – 恢复模式
进入恢复模式 当Leader宕机或者丢失大多数Follower后,即进入恢复模式
结束恢复模式 新领导被选举出来,且大多数Follower完成了与Leader的状态同步后,恢复模式即结束,从 而进入广播模式
恢复模式的意义
发现集群中被commit的proposal的最大zxid
建立新的epoch,从而保证之前的Leader不能再commit新的Proposal
集群中大部分节点都commit过前一个Leader commit过的消息,而新的Leader是被大部分节点所支持的,所 以被之前Leader commit过的Proposal不会丢失,至少被一个节点所保存
新Leader会与所有Follower通信,从而保证大部分节点都拥有最新的数据
恢复阶段的保证
若一条消息在一台机器上被deliver,那么该消息必须将在每台机器上deliver,即使那台机器故障了
一个被skip的消息,必须仍然需要被skip
Zookeeper一致性保证
顺序一致性 从一个客户端发出的更新操作会按发送顺序被顺序执行
原子性 更新操作要么成功要么失败,无中间状态
单一系统镜像 一个客户端只会看到同一个view,无论它连到哪台服务器
可靠性
一旦一个更新被应用,该更新将被持久化,直到有客户端更新该结果
如果一个客户端得到更新成功的状态码,则该更新一定已经生效
任何一个被客户端通过读或者更新“看到”的结果,将不会被回滚,即使是从失败中恢复
实时性 保证客户端可在一定时间(通常是几十秒)内看到最新的视图
Zookeeper使用注意事项
只保证同一客户端的单一系统镜像,并不保证多个不同客户端在同一时刻一定看到同一系统镜像,如果要实现这种效果,需要在读取数据之前调用sync操作
Zookeeper读性能好于写性能,因为任何Server均可提供读服务,而只有Leader可提供写服务
为了保证Zookeeper本身的Leader Election顺利进行,通常将Server设置为奇数
若需容忍f个Server的失败,必须保证有2f+1个以上的Server
Zookeeper的基本操作
四种节点类型 PERSIST, PERSIST_SEQUENTIAL, EPHEMERAL, EPHEMERAL_SEQUENTIAL
可注册Watch操作
Ø Created event: Enabled with a call to exists
Ø Deleted event: Enabled with a call to exists, getData, and getChildren
Ø Changed event: Enabled with a call to exists and getData
Ø Child event: Enabled with a call to getChildren
Watch特征
Ø 客户端先得到通知再得到数据
Ø Watch被fire后即取消,不会再Watch后续变化
基于Zookeeper的Leader Election
抢注Leader节点——非公平模式
1. 创建Leader父节点,如 /chroot,并将其设置为persist节点
2. 各客户端通过在/chroot下创建Leader节点,如/chroot/leader,来竞争Leader。该节点应被设置为ephemeral
3. 若某创建Leader节点成功,则该客户端成功竞选为Leader
4. 若创建Leader节点失败,则竞选Leader失败,在/chroot/leader节点上注册exist的watch,一旦该节点被删除则获
得通知
5. Leader可通过删除Leader节点来放弃Leader
6. 如果Leader宕机,由于Leader节点被设置为ephemeral,Leader节点会自行删除。而其它节点由于在Leader节点
上注册了watch,故可得到通知,参与下一轮竞选,从而保证总有客户端以Leader角色工作
先到先得,后者监视前者——公平模式
1. 创建Leader父节点,如 /chroot,并将其设置为persist节点
2. 各客户端通过在/chroot下创建Leader节点,如/chroot/leader,来竞争Leader。该节点应被设置为
ephemeral_sequential
3. 客户端通过getChildren方法获取/chroot/下所有子节点,如果其注册的节点的id在所有子节点中最小,则当前客户
端竞选Leader成功
4. 否则,在前面一个节点上注册watch,一旦前者被删除,则它得到通知,返回step 3(并不能直接认为自己成为新
Leader,因为可能前面的节点只是宕机了)
5. Leader节点可通过自行删除自己创建的节点以放弃Leader
Leader Election在Curator中的实现
LeaderLatch
1. 竞选为Leader后,不可自行放弃领导权
2.只能通过close方法放弃领导权
3. 强烈建议增加ConnectionStateListener,当连接SUSPENDED或者LOST时视为丢失领导权
4. 可通过await方法等待成功获取领导权,并可加入timeout
5. 可通过hasLeadership方法判断是否为Leader
6. 可通过getLeader方法获取当前Leader
7. 可通过getParticipants方法获取当前竞选Leader的参与方
LeaderSelector
1. 竞选Leader成功后回调takeLeadership方法
2. 可在takeLeadership方法中实现业务逻辑
3. 一旦takeLeadership方法返回,即视为放弃领导权
4. 可通过autoRequeue方法循环获取领导权
5. 可通过hasLeadership方法判断是否为Leader
6. 可通过getLeader方法获取当前Leader
7. 可通过getParticipants方法获取当前竞选Leader的参与方
Kafka“各自为政”Leader Election
“各自为政”Leader Election
每个Partition的多个Replica同时竞争Leader
优点
实现简单
缺点
Herd Effect
Zookeeper负载过重
Latency较大
Kafka基于Controller的Leader Election
基于Controller的Leader Election
整个集群中选举出一个Broker作为Controller
Controller为所有Topic的所有Partition指定Leader及Follower
优点
极大缓解Herd Effect问题
减轻Zookeeper负载
Controller与Leader及Follower间通过RPC通信,高效且实时
缺点
引入Controller增加了复杂度
需要考虑Controller的Failover