zookeeper HA 实现主备切换

一、思路

在这里提供一张主备切换的思路图。这张图非常重要,理解这张图就基本明白主备切换的思路了


  1. 启动server时注册一个临时的有序的子节点(注意,一定要是临时有序的),将自己注册的子节点保存在一个全局变量中
  2. 获取父节点下所有的子节点,排序,然后将自己的节点与最小子节点比较,如果相等则成为主机,不相等则等待。
  3. 实现Watcher接口,当父节点发生变化时,执行 1、2
二、代码示例
package com.newcosoft.lsmp.bank.schedule;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import com.newcosoft.lsmp.bank.client.config.BankConfig;
import com.newcosoft.lsmp.bank.client.constant.BankConstant;
import com.newcosoft.lsmp.bank.client.zookeeper.BankZookHelper;
import com.newcosoft.lsmp.bank.schedule.config.BankDatabaseConfig;
import com.newcosoft.lsmp.bank.schedule.util.ApplicationContextUtil;
import com.newcosoft.util.StringUtils;

public class BankScheduleStart implements Watcher {

private static Logger logger = LoggerFactory
.getLogger(BankScheduleStart.class);

private ApplicationContext applicationContext;

private CountDownLatch signal = new CountDownLatch(1);

private ZooKeeper zk;

private String myNode;

private String parentNode = BankConfig.getInstance()
.getBankScheduleParentNode();

public static void main(String[] args) {
try {
      BankScheduleStart bankScheduleStart = new BankScheduleStart();
bankScheduleStart.registerZook();
bankScheduleStart.getLock();
} catch (KeeperException e) {
logger.error(e.toString());
} catch (InterruptedException e) {
logger.error(e.toString());
}
}

// 获取zk连接
private void initZk() {
try {
if (zk == null || !zk.getState().isAlive()) {
synchronized (this) {
if (zk != null) {
zk.close();
}
// 重新建立连接
zk = new ZooKeeper(BankConfig.getInstance().getZookURL(),
BankConstant.HA_SESSION_TIMEOUT, this);
while (zk.getState() != ZooKeeper.States.CONNECTED) {
Thread.sleep(3000);
}
}
}
} catch (Exception e) {
logger.error("zk初始化连接异常:" + e.toString());
}
}

// 注册节点
private void registerZook() {
// 初始化zk
initZk();
// 父节点路径
String[] nodeList = parentNode.split("/");
String nodePath = "";
// 循环创建持久父节点
for (String node : nodeList) {
if (!StringUtils.isEmpty(node)) {
nodePath = nodePath + "/" + node;
BankZookHelper.createNode(zk, nodePath, node,
CreateMode.PERSISTENT);
}
}
// 创建临时子节点,这里注意
myNode = BankZookHelper.createNode(zk, nodePath + "/"
+ BankConstant.HA_BANK_SCHEDULE_NODE_NAME, "",
CreateMode.EPHEMERAL_SEQUENTIAL);
}

// 启动bank-schedule 主业务的核心代码
public void doAction() {
try {
if (applicationContext != null) {
logger.debug("服务器已经启动!");
return;
}
BankDatabaseConfig.init();
applicationContext = ApplicationContextUtil.getInstance()
.getApplicationContext(
"/spring/springContext_lsmp_bank.xml");
if (logger.isDebugEnabled()) {
logger.debug("服务器启动!");
logger.debug("-----BANK SCHEDULE START FINISH-----");
}
} catch (Exception e) {
logger.error("-----BANK SCHEDULE START FAILED-----", e);
System.exit(1);
}
}

// 获得lock
public void getLock() throws KeeperException, InterruptedException {
// 获取父节点下所有的子节点
List list = zk.getChildren(parentNode, true);
String[] nodes = list.toArray(new String[list.size()]);
// 将子节点排序
Arrays.sort(nodes);
// 比较是不是自己创建
if (myNode.equals(parentNode + "/" + nodes[0])) {
doAction();
} else {
waitForLock(nodes[0]);
}

}

// 等待锁
void waitForLock(String lower) throws KeeperException, InterruptedException {
// 看最小的节点是否还存在,存在表明主机是好的,则一直等到主机down掉
Stat stat = zk.exists(parentNode + "/" + lower, true);
if (stat != null) {
logger.debug("服务器等待中。。。。。");
signal.await();
} else {
getLock();
}
}

@Override
public void process(WatchedEvent event) {
if (event.getType() == EventType.NodeChildrenChanged) {
try {
logger.debug("触发子节点改变事件");
getLock();
} catch (Exception e) {
logger.error(e.toString());
}
}
if (event.getState() == KeeperState.Disconnected) {
try {
logger.debug("触发了断开连接事件");
logger.debug("退出程序");
System.exit(0);
} catch (Exception e) {
logger.error(e.toString());
}
}
}
}


猜你喜欢

转载自blog.csdn.net/Hy_Rvier/article/details/38823425