基于zookeeper的主备服务

摘要

为了高可用通产我们一个服务会部署多节点。但是有时我们希望对某些操作要求单线程处理,此时可以通过主备服务形式实现。正常情况下主节点服务处理,当主节点宕机后备用节点相关服务继续处理。

需求

资源中心会将资源文件相关操作分发至各个项目节点,资源的操作包括添加、分享、删除、和谐、恢复。资源中心向mq发布消息,各个项目节点通过订阅mq消息处理对应资源。各项目节点HA部署双节点。此时有可能出现其中一个节点正在处理资源的上传操作,由于涉及到文件IO,此操作比较慢,同时另一个节点接收到了删除的mq消息,执行对应的资源删除操作。为了避免这种情况,要求对消息的操作必须是顺序的。

解决方案

项目的两个节点在启动完成后抢注zookeeper同时订阅zk的节点disconnect事件,抢注成功的节点成为主节点通知执行启动操作,抢注失败的节点为从节点不做操作。当主节点宕机后,从节点会接收到disconnect消息,从而执行启动操作。

实现

  1. 服务启动后抢注zk
    @Override
    public void run(ApplicationArguments args) throws Exception {
        if(enable){
            zookeeperWatcher.connect();
            if(!zookeeperWatcher.exists()){
                zookeeperWatcher.create();
            }else{
                log.info("节点已被抢注,不再发布节点抢注成功事件!!!");
            }
        }else{
            log.info("不启用,默认发布抢注成功事件");
            SpringContextUtil.getApplicationContext().publishEvent(new MainStandbyRunnerEvent(true));
        }
    }
    
  2. 通过publishEvent+listener将主备启动与主备解耦
    抢注zk节点成功发布启动事件
    SpringContextUtil.getApplicationContext().publishEvent(new MainStandbyRunnerEvent(true));
    
    各个业务方通过添加listener实现启动逻辑
    @Override
    public void onApplicationEvent(MainStandbyRunnerEvent event) {
        log.info("执行节点服务启动时间!");
        //TODO 启动逻辑
        log.info("节点服务启动完成!");
    }
    
  3. 监听zk节点断开事件并注册节点发布注册成功事件
    @Override
    public void process(WatchedEvent event){
        if(path.equals(event.getPath()) && event.getType() == Event.EventType.NodeDeleted){
            log.info("节点断开连接");
           	//抢注节点
            create();
        }
    }
    /**
     * 创建节点并发布事件
     */
    public void create(){
        try {
            byte[] bytes = value.getBytes(CHARSET);
            this.zookeeper.create(path, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            log.info("发布节点抢注成功事件");
            SpringContextUtil.getApplicationContext().publishEvent(new MainStandbyRunnerEvent(true));
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
    

效果

主节点启动

2020-01-13 16:24:02.196 [main] INFO  c.i.s.common.mainstandby.config.MainStandbyProperties - 主备启动参数:[enable=true,host=127.0.0.1,path=/MainStandbyStart,value=MainNodeStart]
2020-01-13 16:26:36.127 [main] INFO  c.i.sclass.common.mainstandby.watcher.ZookeeperWatcher - 发布节点抢注成功事件
2020-01-13 16:26:36.127 [main] INFO  c.i.sclass.baseauth.listener.MainStandbyRunnerListener - 执行节点服务启动事件!
2020-01-13 16:26:36.127 [main] INFO  c.i.sclass.baseauth.listener.MainStandbyRunnerListener - 节点服务启动完成!

从节点启动

2020-01-13 16:27:46.292 [main] INFO  c.i.s.common.mainstandby.config.MainStandbyProperties - 主备启动参数:[enable=true,host=127.0.0.1,path=/MainStandbyStart,value=StandbyNodeStart]
2020-01-13 16:27:46.762 [main] INFO  c.i.s.common.mainstandby.component.MainStandbyRunner - 节点已被抢注,不再发布节点抢注成功事件!!!

主节点宕机

2020-01-13 16:28:39.509 [main-EventThread] INFO  c.i.sclass.common.mainstandby.watcher.ZookeeperWatcher - 节点断开连接
2020-01-13 16:28:39.516 [main-EventThread] INFO  c.i.sclass.common.mainstandby.watcher.ZookeeperWatcher - 发布节点抢注成功事件
2020-01-13 16:28:39.516 [main-EventThread] INFO  c.i.s.baseauthtest.listener.MainStandbyRunnerListener - 执行节点服务启动事件!
2020-01-13 16:28:39.517 [main-EventThread] INFO  c.i.s.baseauthtest.listener.MainStandbyRunnerListener - 节点服务启动完成!
发布了23 篇原创文章 · 获赞 27 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u014395955/article/details/103959127