(十)soul源码的zookeeper同步数据

(十)soul源码的zookeeper同步数据

目标

  • soul-admin怎么开启zookeeper和初始化相应类
  • soul-bootstrap端怎么获取数据
  • 官方提供的流程图

soul-admin怎么开启zookeeper和初始化相应类

  • 首先本地需要启动zookeeper服务

  • soul-admin的配置,要把websocket注释掉

soul:
 database:
   dialect: mysql
   init_script: "META-INF/schema.sql"
 sync:
   zookeeper:
#    websocket:
#      enabled: true
     url: localhost:2181
     sessionTimeout: 5000
     connectionTimeout: 2000
  • DataSyncConfiguration的内部类,ZookeeperListener初始化,ZookeeperDataChangedListener和ZookeeperDataInit都在这里初始化
    /**
     * The type Zookeeper listener.
     */
    @Configuration
    @ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
    @Import(ZookeeperConfiguration.class)
    static class ZookeeperListener {

        /**
         * Config event listener data changed listener.
         *
         * @param zkClient the zk client
         * @return the data changed listener
         */
        @Bean
        @ConditionalOnMissingBean(ZookeeperDataChangedListener.class)
        public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) {
            return new ZookeeperDataChangedListener(zkClient);
        }

        /**
         * Zookeeper data init zookeeper data init.
         *
         * @param zkClient        the zk client
         * @param syncDataService the sync data service
         * @return the zookeeper data init
         */
        @Bean
        @ConditionalOnMissingBean(ZookeeperDataInit.class)
        public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) {
            return new ZookeeperDataInit(zkClient, syncDataService);
        }
    }

ZookeeperDataChangedListener和WebsocketDataChangedListener类似,同样实现了 DataChangedListener 接口,用于具体的事件监听,并处理数据,存在zkclient成员变量,通过zkclient进行操作数据
ZookeeperDataInit实现了 CommandLineRunner 接口,项目启动后执行run方法:项目启动时把所以配置数据刷新到zookeeper上,

    @Override
    public void run(final String... args) {
        String pluginPath = ZkPathConstants.PLUGIN_PARENT;
        String authPath = ZkPathConstants.APP_AUTH_PARENT;
        String metaDataPath = ZkPathConstants.META_DATA;
        if (!zkClient.exists(pluginPath) && !zkClient.exists(authPath) && !zkClient.exists(metaDataPath)) {
            syncDataService.syncAll(DataEventTypeEnum.REFRESH);
        }
    }
    
//SyncDataServiceImpl的syncAll,
    @Override
    public boolean syncAll(final DataEventTypeEnum type) {
        appAuthService.syncData();
        List<PluginData> pluginDataList = pluginService.listAll();
        eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList));
        List<SelectorData> selectorDataList = selectorService.listAll();
        eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList));
        List<RuleData> ruleDataList = ruleService.listAll();
        eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList));
        metaDataService.syncData();
        return true;
    }


通过spring发布事件,调用不同的具体的事件监听并处理数据 (spring发布事件和监听在第七小节有详细讲解)
我们在admin的客户端修改了插件、选择器、规则、权限等通过spring事件监听来到这里ZookeeperDataChangedListener
如下只贴了规则修改的代码

    @Override
    public void onRuleChanged(final List<RuleData> changed, final DataEventTypeEnum eventType) {
        if (eventType == DataEventTypeEnum.REFRESH) {
            final String selectorParentPath = ZkPathConstants.buildRuleParentPath(changed.get(0).getPluginName());
            deleteZkPathRecursive(selectorParentPath);
        }
        for (RuleData data : changed) {
            final String ruleRealPath = ZkPathConstants.buildRulePath(data.getPluginName(), data.getSelectorId(), data.getId());
            if (eventType == DataEventTypeEnum.DELETE) {
                deleteZkPath(ruleRealPath);
                continue;
            }
            final String ruleParentPath = ZkPathConstants.buildRuleParentPath(data.getPluginName());
            createZkNode(ruleParentPath);
            //create or update
            upsertZkNode(ruleRealPath, data);
        }
    }

//createZkNode,upsertZkNode,deleteZkPath等zookeeper的操作
    private void upsertZkNode(final String path, final Object data) {
        if (!zkClient.exists(path)) {
            zkClient.createPersistent(path, true);
        }
        zkClient.writeData(path, data);
    }
  • 下图是项目初始化后,在zookeeper创建的节点数据
    在这里插入图片描述

ZookeeperListener的 @Import(ZookeeperConfiguration.class) ,ZookeeperConfiguration里面会初始化ZkClient,使用ZKClient这个Zookeeper客户端
ZKClient存在api:1.创建会话
2.创建节点
3.读取数据
4.更新数据
5.删除节点
6.检查节点是否存在

   @Bean
     @ConditionalOnMissingBean(ZkClient.class)
     public ZkClient zkClient(final ZookeeperProperties zookeeperProp) {
         return new ZkClient(zookeeperProp.getUrl(), zookeeperProp.getSessionTimeout(), zookeeperProp.getConnectionTimeout());
     }

soul-bootstrap端怎么获取数据

  • yml配置
soul :
   file:
     enabled: true
   corss:
     enabled: true
   dubbo :
     parameter: multi
   sync:
       zookeeper:
            url: localhost:2181
            sessionTimeout: 5000
            connectionTimeout: 2000
  • 依赖
   <dependency>
       <groupId>org.dromara</groupId>
       <artifactId>soul-spring-boot-starter-sync-data-zookeeper</artifactId>
       <version>${project.version}</version>
   </dependency>
  • 初始化相应类

根据配置信息,在ZookeeperSyncDataConfiguration初始化ZookeeperSyncDataService和ZkClient

   @Bean
    public SyncDataService syncDataService(final ObjectProvider<ZkClient> zkClient, final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
                                           final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers, final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
        log.info("you use zookeeper sync soul data.......");
        return new ZookeeperSyncDataService(zkClient.getIfAvailable(), pluginSubscriber.getIfAvailable(),
                metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));
    }
  • ZookeeperSyncDataService:对数据

构造函数:watcherData():刷新本地jvm缓存,其他两个方式类似
watch机制,监听zookeeper数据的变化

   public ZookeeperSyncDataService(final ZkClient zkClient, final PluginDataSubscriber pluginDataSubscriber,
                                   final List<MetaDataSubscriber> metaDataSubscribers, final List<AuthDataSubscriber> authDataSubscribers) {
       this.zkClient = zkClient;
       this.pluginDataSubscriber = pluginDataSubscriber;
       this.metaDataSubscribers = metaDataSubscribers;
       this.authDataSubscribers = authDataSubscribers;
       watcherData();
       watchAppAuth();
       watchMetaData();
   }

   private void watcherData() {
       final String pluginParent = ZkPathConstants.PLUGIN_PARENT;
       // 获取插件列表,启动监听(在其中同时进行插件数据、选择器、规则的初始化)
       List<String> pluginZKs = zkClientGetChildren(pluginParent);
       for (String pluginName : pluginZKs) {
           watcherAll(pluginName);
       }
       
       // 启动监听,数据更新(新增和修改),对更新的插件进行监听
       zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> {
           if (CollectionUtils.isNotEmpty(currentChildren)) {
               for (String pluginName : currentChildren) {
                   watcherAll(pluginName);
               }
           }
       });
   }

//watch机制,监听数据的变化
   private void watcherAll(final String pluginName) {
       watcherPlugin(pluginName);
       watcherSelector(pluginName);
       watcherRule(pluginName);
   }

官方提供的流程图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

  • zookeeper主要是依赖 watch 机制,soul-web 会监听配置的节点,soul-admin 在启动的时候,会将数据全量写入 zookeeper,后续数据发生变更时,会增量更新 zookeeper 的节点,与此同时,soul-web 会监听配置信息的节点,一旦有信息变更时,会更新本地缓存
  • zookeeper相对http长轮训简单好理解,不是分组更新数据,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值