zookeeper在mysql主库选举切换中的应用

来源:互联网 发布:中国移动终端大数据 编辑:程序博客网 时间:2024/06/05 18:11

为了保证可用性,传统的关系型数据库(mysql)通常采用一主多备的方式,当主宕机后,切换到备机。

有多种方式可以做到主备自动失效检测切换,比如传统的HA软件heartbeat,keepalived等等,采取虚拟IP的方式对客户端透明;

这里主备自动切换方案没有采取VIP的方式,而是用zookeeper对数据库集群进行管理,可以做到主备失效检测切换,主库的选举,

比如考虑到mysql对多核利用率不高而一个节点部署多个mysql实例,如果采用VIP的方式是不可行的。

基本做法是,每个mysql实例上部署一个agent节点,agent负责对该mysql实例定时进行ping操作,agent在zookeeper上注册临时节点,

如果agent或者该agent代理的mysql宕掉,注册在zookeeper上的节点就会发生变化,利用zookeeper watch功能实现mysq实例失效后的主库的选举操作,

读节点也通过watch master节点的功能实现指向新的master。

如上图所示,zookeeper上的master节点存储当前主节点名称;

servers下面保存在线的节点,命名规则一般为ip:port_序列号;

nodeid为当前的序列号,每当节点获取一个序列号时,该节点值自就增,以便于分配给下一个节点。

 过程如下:

 a、初始化阶段,创建servers,master,nodeid节点

 b、每个client创建servers子节点,zoo_create("/servers/xxx",EPHEMERAL)

 c、zoo_get_child(/servers,NULL)

 d、若当前client的序列号id是当前最小的节点,则当前节点是master,设置master节点退出

 e、否则,zoo_exsists(lastid before id,watcher)//当前节点watch比当前节点id次小的那个节点的状态

                  如果id不存在,那么退出

                   否则等待watch触发,重新选举master

相关伪代码参考示例如下:

 

try {
   ProposoWatcher wc = new ProposoWatcher();
   final ZooKeeper zk = new ZooKeeper("10.1.1.24:2181", 60000, wc);
   wc.setZk(zk);
   if (zk.exists("/master", false) == null) {
    zk.create("/master", null, Ids.OPEN_ACL_UNSAFE,
      CreateMode.PERSISTENT);
   }
   zk.create("/servers/" + nodeName, new byte[0], Ids.OPEN_ACL_UNSAFE,
     CreateMode.EPHEMERAL);

   List<String> list = zk.getChildren("/servers", false);
   String[] nodes = list.toArray(new String[list.size()]);
   Arrays.sort(nodes);
   if (nodeName.equals(nodes[0])) {//初次
     System.out.println("this is master" + nodes[0]);
     zk.setData("/master", nodeName.getBytes(), -1);
    
   } else {
    // 监控比自己次小的Node
    String lower = "";
    for (int i = 0; i < nodes.length; i++) {
     if (nodeName.equals(nodes[i])) {
      lower = nodes[i - 1];
      break;
     }
    }
    zk.exists("/servers/" + lower, true);
   }

  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

//watcher

 public static class ProposoWatcher implements Watcher {
  private ZooKeeper zk;

  public ZooKeeper getZk() {
   return zk;
  }

  public void setZk(ZooKeeper zk) {
   this.zk = zk;
  }

  @Override
  public void process(WatchedEvent event) {
   // TODO Auto-generated method stub
   switch (event.getType()) {
   case None: {
    // connectionEvent(event);
    break;
   }
   case NodeDeleted: {
    // servers.remove(event.getPath());
    System.out.println("node  removed**" + event.getPath());
    String delName = event.getPath().substring(
      event.getPath().lastIndexOf("/"));
    // final ZooKeeper zk1 = zk;
    List<String> children = new ArrayList<String>();
    try {
     children = zk.getChildren("/servers", false);
    } catch (KeeperException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    String[] nodes = children.toArray(new String[children.size()]);
    Arrays.sort(nodes);
    for (int i = 0; i < nodes.length; i++) {
     if (nodes[i].compareTo(delName) > 0) {
      // TODO:curr Node become master
      System.out.println("this is master" + nodes[i]);
      try {
       zk.setData("/master", nodes[i].getBytes(), -1);
      } catch (KeeperException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      } catch (InterruptedException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
      break;
     }
    }
    break;
   }
   }
  }

 }

 

 

 

 

原创粉丝点击