04zookeeper场景应用-master选举

master选举

考虑到7x24向外提供服务的系统,不能有单点故障,于是我们使用集群,采用的是master-salve模式。集群中有一台主机和多台备机,其中主机向外提供服务,备机监听主机状态,一旦主机宕机,备机必须迅速接管主机继续向外提供服务。在这个过程中,从备机中选出一台作为主机的过程称为master选举

架构图

04zookeeper场景应用-master选举_第1张图片
左边是zookeeper集群,右边是工作服务器。工作服务器启动的时候会去zookeeper的servers节点下进行创建临时节点,并把基本信息写入节点。这个过程叫做服务注册。系统中的其他服务可以通过获取servers节点下所有的自节点可以得知当前系统有哪些服务可用,这个过程叫做服务发现。接着这些工作服务器会尝试创建一个master节点,谁创建成功谁就是master其余的节点就是salve。同时所有的服务器必须监听master节点的删除事件。通过监听master节点的删除事件,来了解master节点是否宕机。一旦master宕机开始新一轮的master选举。

流程图

04zookeeper场景应用-master选举_第2张图片

核心类图

04zookeeper场景应用-master选举_第3张图片

WorkServer对应架构图的WorkServer,是主工作类;

RunningData用来描述WorkServer的基本信息;

LeaderSelectorZkClient作为调度器来启动和停止WorkServer;

代码实现

/**
 * Created by zhangdd on 2019/12/17
 * 

* 工作服务器信息 */ public class RunningData implements Serializable { private static final long serialVersionUID = 4478643441552034827L; private Long cid; private String name; public Long getCid() { return cid; } public void setCid(Long cid) { this.cid = cid; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

/**
 * Created by zhangdd on 2019/12/17
 * 

* 工作服务器 */ public class WorkServer { // 记录服务器状态 private volatile boolean running = false; private ZkClient zkClient; // Master节点对应zookeeper中的节点路径 private static final String MASTER_PATH = "/master"; // 监听Master节点删除事件 private IZkDataListener dataListener; // 记录当前节点的基本信息 private RunningData serverData; // 记录集群中Master节点的基本信息 private RunningData masterData; private ScheduledExecutorService delayExector = Executors.newScheduledThreadPool(1); private int delayTime = 5; public WorkServer(RunningData rd) { //注册server节点 //尝试写master节点, this.serverData = rd; this.dataListener = new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { } @Override public void handleDataDeleted(String s) throws Exception { // if (masterData != null && masterData.getName().equals(serverData.getName())) { // // 自己就是上一轮的Master服务器,则直接抢 // takeMaster(); // } else { // 否则,延迟5秒后再抢。主要是应对网络抖动,给上一轮的Master服务器优先抢占master的权利,避免不必要的数据迁移开销 delayExector.schedule(new Runnable() { public void run() { takeMaster(); } }, delayTime, TimeUnit.SECONDS); // } } }; } public ZkClient getZkClient() { return zkClient; } public void setZkClient(ZkClient zkClient) { this.zkClient = zkClient; } // 停止服务器 public void stop() throws Exception { if (!running) { throw new Exception("server has stoped"); } running = false; delayExector.shutdown(); // 取消Master节点事件订阅 zkClient.unsubscribeDataChanges(MASTER_PATH, dataListener); // 释放Master权利 releaseMaster(); } public void start() throws Exception { if (running) { throw new Exception("server has start up ..."); } running = true; //订阅master删除事件 zkClient.subscribeDataChanges(MASTER_PATH, dataListener); //争抢master权利 takeMaster(); } private void takeMaster() { if (!running) { return; } try { zkClient.createEphemeral(MASTER_PATH, serverData); masterData = serverData; System.out.println("我是 "+serverData.getName() + " 我抢到了 master"); //作为演示,这里让master服务器每隔5秒释放一次Master权利 delayExector.schedule(new Runnable() { @Override public void run() { if (checkMaster()) { System.out.println("我是master " + masterData.getName() + " 我要销毁了 你们准备好抢夺啊"); releaseMaster(); } } }, 5, TimeUnit.SECONDS); } catch (ZkNodeExistsException e) {//节点已经存在 // System.out.println(e.getMessage() + "\n" + serverData.getName()); //读取Master节点信息 RunningData runningData = zkClient.readData(MASTER_PATH); if (runningData == null) { takeMaster(); // 没读到,读取瞬间Master节点宕机了,有机会再次争抢 } else { masterData = runningData; } } catch (Exception e) { e.printStackTrace(); } } // 释放Master权利 private void releaseMaster() { if (checkMaster()) { zkClient.delete(MASTER_PATH); } } /** * 检测当前是否是master * * @return */ private boolean checkMaster() { try { RunningData eventData = zkClient.readData(MASTER_PATH); masterData = eventData; if (masterData.getName().equals(serverData.getName())) { return true; } return false; } catch (Exception e) { return false; } } }

/**
 * Created by zhangdd on 2019/12/17
 * 

* 调度器 */ public class LeaderSelectorZkClient { //启动的服务个数 private static final int CLIENT_QTY = 10; //zookeeper服务器的地址 private static final String ZOOKEEPER_SERVER = "192.168.0.119:2181"; public static void main(String[] args) { //保存所有zkClient的列表 List clients = new ArrayList<>(); //保存所有服务的列表 List workServers = new ArrayList(); try { for (int i = 0; i < CLIENT_QTY; i++) { //创建zkclient ZkClient zkClient = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new SerializableSerializer()); clients.add(zkClient); RunningData runningData = new RunningData(); runningData.setCid(Long.valueOf(i)); runningData.setName("Client #" + i); //创建服务 WorkServer workServer = new WorkServer(runningData); workServer.setZkClient(zkClient); workServers.add(workServer); workServer.start(); } System.out.println("敲入回车键退出 \n"); new BufferedReader(new InputStreamReader(System.in)).readLine(); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("Shutting down..."); for (int i = 0; i < workServers.size(); i++) { try { workServers.get(i).stop(); } catch (Exception e) { e.printStackTrace(); } } for (int i = 0; i < clients.size(); i++) { try { clients.get(i).close(); } catch (Exception e) { e.printStackTrace(); } } } } }

04zookeeper场景应用-master选举_第4张图片

你可能感兴趣的:(zookeeper)