UDN-企业互联网技术人气社区

板块导航

浏览  : 1339
回复  : 0

[其它] Zookeeper实现分布式选举算法

[复制链接]
呵呵燕的头像 楼主
发表于 2016-9-18 22:09:07 | 显示全部楼层 |阅读模式
  Leader选举

  分布式系统中经常采用Master/Slave架构。(截止到目前为止我还没有碰到过其他架构。。。)这种架构中如果Master发生故障就会导致整个集群停止服务,为了提高系统的高可用性通常采用选举算法来选出Master。这样Master如果出现故障,Slave经过选举算法重新选择Master。

  通过Zookeeper可以非常容易实现这个功能,关键代码如下:(完整代码见文章最后的GitHub地址)

  1. //申请做 leader
  2. String prefix = "/ticket-";
  3. String myVote = zooKeeper.create(root + prefix, new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
  4. //获取所有参选人
  5. List<String> allVote = zooKeeper.getChildren(root, new Watcher() {…………});
  6. //寻找最小的id,谁最小谁是leader
  7. String minVotePath = allVote.get(0);
  8. String minVote = fixForSorting(prefix, minVotePath);
  9. for (String vote : allVote) {
  10.     String thisVoteId = fixForSorting(prefix, vote);
  11.     if (thisVoteId.compareTo(minVote) < 0) {
  12.         minVotePath = vote;
  13.         minVote = thisVoteId;
  14.     }
  15. }
  16. LOGGER.debug("当前leader {}", minVotePath);
复制代码


  选举过程非常简单,分为三步

  在zookeeper上新增一个节点作为自己的选票(相当于自己指定自己做leader)。比如例子中我的root节点选择的是/ha,所有的服务器启动后都会在这个节点下新增一个ticket为前缀的新节点。这是一个比较特殊的节点,通过指定EPHEMERAL_SEQUENTIAL可以让Zookeeper帮我们给节点新增一串数字。(比如第一台启动的服务器得到的是ticket-0000000002、第二台得到的是ticket-0000000003)

  获取所有选票,(比如例子中通过getChildren方法获取/ha下面所有的节点)

  比较选票,如果自己的选票是最小的,说明自己被选中做leader,(判断是否是leader的规则是id最小)否则就认为自己没有当选,等待节点变化迎接下次选举(通过Watcher对象)。

  分析一下这个算法不难发现,如果有3台服务器启动,第一个向zookeeper“报告”的人会被当选为leader;如果它出现故障,第二个向zookeeper“报告”的人会被当选为leader,以此类推。这是一种非常原始的民主选举制度,有一个象征最高权力的“神器”,得到“神器”的就是大部落的酋长;很多人想要参选大酋长,那么谁跑得快最先抢到“神器”谁就是大酋长;如果在后面的“执政”期间酋长因为“太堕落”被干掉了那么第二名自动接管“神器”变成大酋长。

  把上面的代码执行两次,最先执行的程序会被选择为leader;杀死第一个进程,第二个进程的控制台会输出自己当选为leader的信息。(第二个进程不是立即输出信息,需要等待几秒钟)

  Zookeeper Watcher

  Watcher是一个接口,它的定义很简单

  1.  public interface Watcher {
  2.     abstract public void process(WatchedEvent event);
  3. }
复制代码


  一个典型的Callback,当发生事件的时候(WatchedEvent)由系统会调用process方法。

  以Zookeeper Java Client为例,我们会在两个地方用到Watcher对象

  new ZooKeeper的时候需要传一个Watcher对象,在客户端连接到Zookeeper服务器或者断开连接或者Session过期的时候都会调用Watcher对象。这种情况下我们关注WatchedEvent中的keeperState成员变量,它是一个枚举类型,可以是:Disconnected、SyncConnected、AuthFailed、ConnectedReadOnly、Expired、SaslAuthenticated等。

  调用getChildren的时候需要传一个Watcher对象,某个数据节点发送变化的时候服务器会推送消息给客户端,此时Watcher对象就会被调用。这种情况下我们关注eventType成员变量,它是一个枚举类型,可以是:NodeCreated、NodeDeleted、NodeDataChanged、NodeChildrenChanged等。总结下来,一种Watcher用来监控Zookeeper的连接;一种Watcher用来监控数据的变化。

  注意

  Leader和Zookeeper之间是有心跳数据的,时间间隔是sessionTimeout决定的。

  EPHEMERAL_SEQUENTIAL的含义有两个:EPHEMERAL,表示节点是临时的,当zookeeper和客户端断开连接的时候节点会被阐述;SEQUENTIAL,zookeeper会在节点最后加上一串数字后缀。

  所以重新选举的时间是实际上是由sessionTimeout决定的,zookeeper服务器探测到客户端断开后才会删除临时节点,推送变化,此时选举才会进行。

  完整代码 https://gist.github.com/fireflyc/51d5467ef48b7f8c4a7747e5ecbd3fd0
原文作者: fireflyc  来源:开发者头条

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部