生产环境中一般都要实现高可用,保证集群中其中一个节点down掉之后,集群还能正常运行,并且有些业务只能在主节点上运行,其他节点只是处于备用状态。
项目中的高可用是基于Zookeeper实现,采用的是主从方式,一台节点作为Master,另外几台节点作为Slave。实际使用ZK开发的话,一般会使用Curator框架,它是由Netflix公司贡献给Apache的,主要是封装了ZK的客户端,方便开发人员的使用。本文基于ZK写了一个高可用的简单Demo。
基于Curator的选主策略:
有两种选主策略:LeaderLatch 和 LeaderSelector。
LeaderLatch采用的是抢占的方式来选主(非公平),一旦选出Leader,除非有客户端挂掉重新触发选举或者主动调用close()方法,否则不会交出Leadership。
LeaderSelector则是每个客户端都能公平获取Leadership,但是选举出来的Leader不会一直占用领导权,当takeLeadership()方法结束后就会自动释放领导权。
综上可以得出,LeaderLatch更加适合HA这种方式,本文后续分析的就是此种方法,LeaderSelector后续用到了再说。
LeaderLatch方式选举主节点:
图示如下:
创建的是Ephemeral + Sequence节点。
Ephemeral节点如果创建它的客户端和服务器之间的Session结束会被自动删除。(Persist节点则不会)。
Sequence节点则是多个客户端创建同一名称节点时,都会创建成功,只是节点名称后面会跟上0,1,2,3这样的序号(Non-Sequence节点则是当多个客户端创建同一个Non-Sequence节点时,只有一个可以创建成功,并且节点名称可创建时指定的节点名称完全一样)。
基于SpringBoot实现的一个小Demo:
@SpringBootApplication public class SpringBootApplicationDemo1 { //todo 这个类不能直接放到src目录下面 public static void main(String[] args) { SpringApplication.run(SpringBootApplicationDemo1.class, args); LeaderLatchContext.INSTANCE.startLeaderLatch(); } }
public class LeaderLatchContext { public static final LeaderLatchContext INSTANCE = new LeaderLatchContext(); //主节点竞争客户端初始化标识(服务器刚部署的时候才用到这个属性) private boolean leaderLatchInitedInd = false; /** * 主节点竞争类 */ private LeaderLatch leaderLatch; /** * 主节点竞争时给竞争客户端用 */ private CuratorFramework zkClient = ZookeeperUtil.getClient("10.10.23.37:2181"); /** * 主节点竞争路径 */ private static final String latchPath = "/leaderlatch"; /** * 基于ZK进行主从节点选举(主节点挂了之后,其他节点能切换过来,但是时间有点长) */ public void startLeaderLatch() { //防止主从之间来回切换 Lock lock = new ReentrantLock(); if (!leaderLatchInitedInd) { //初始化竞争类 //LeaderLatch.CloseMode.NOTIFY_LEADER: When the latch is closed, listeners *will* be notified leaderLatch = new LeaderLatch(zkClient, latchPath, "123456789", LeaderLatch.CloseMode.NOTIFY_LEADER); //加入监听机制 leaderLatch.addListener(new LeaderLatchListener() { @Override public void isLeader() { try { lock.lock(); System.out.println("=========================================I am Leader========================================="); System.out.println(leaderLatch.getId()); } finally { lock.unlock(); } } @Override public void notLeader() { try { lock.lock(); System.out.println("=========================================I am not Leader========================================="); System.out.println(leaderLatch.getId()); } finally { lock.unlock(); } } }); //开始主节点竞争 try { leaderLatch.start(); } catch (Exception e) { System.out.println("Leader Latch start error"); e.printStackTrace(); } leaderLatchInitedInd = true; } else { System.out.println("Leader has already init."); } } }
后续有时间再好好探究一下ZK内部的原理…感觉遗留了好多坑没时间填,慢慢填吧
参考:
http://ifeve.com/zookeeper-leader/(学习Zookeeper的Leader选举)
https://blog.csdn.net/qq_34021712/article/details/82880062(Zookeeper的两种选举方式图示)
https://segmentfault.com/a/1190000016072834(基于ZK的分布式锁)