考虑到7x24向外提供服务的系统,不能有单点故障,于是我们使用集群,采用的是master-salve模式。集群中有一台主机和多台备机,其中主机向外提供服务,备机监听主机状态,一旦主机宕机,备机必须迅速接管主机继续向外提供服务。在这个过程中,从备机中选出一台作为主机的过程称为master选举
左边是zookeeper集群,右边是工作服务器。工作服务器启动的时候会去zookeeper的servers
节点下进行创建临时节点,并把基本信息写入节点。这个过程叫做服务注册。系统中的其他服务可以通过获取servers
节点下所有的自节点可以得知当前系统有哪些服务可用,这个过程叫做服务发现。接着这些工作服务器会尝试创建一个master
节点,谁创建成功谁就是master
其余的节点就是salve
。同时所有的服务器必须监听master
节点的删除事件。通过监听master
节点的删除事件,来了解master
节点是否宕机。一旦master
宕机开始新一轮的master
选举。
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();
}
}
}
}
}