Soul-源码阅读11-Zookeeper同步数据

ZK同步原理

基于 zookeeper 的同步原理很简单,主要是依赖 zookeeper 的 watch 机制,soul-web 会监听配置的节点,soul-admin 在启动的时候,会将数据全量写入 zookeeper,后续数据发生变更时,会增量更新 zookeeper 的节点,与此同时,soul-web 会监听配置信息的节点,一旦有信息变更时,会更新本地缓存。

DEMO启动

soul-admin配置

  • 在application.yml 注释掉websocket的开关,开启zk同步配置。

    soul:
      database:
        dialect: mysql
        init_script: "META-INF/schema.sql"
        init_enable: true
      sync:
    #    websocket:
    #      enabled: true
          zookeeper:
              url: localhost:2181
              sessionTimeout: 5000
              connectionTimeout: 2000
    

soul-bootstrap配置

  • 在application-local.yml,注释掉websocket的配置,开启zk同步方式。
soul :
    file:
      enabled: true
    corss:
      enabled: true
    dubbo :
      parameter: multi
    sync:
#        websocket :
#             urls: ws://localhost:9095/websocket

        zookeeper:
             url: localhost:2181
             sessionTimeout: 5000
             connectionTimeout: 2000
  • pom.yml 引入依赖(默认引入)
<!--soul data sync start use zookeeper-->
<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>soul-spring-boot-starter-sync-data-zookeeper</artifactId>
    <version>${project.version}</version>
</dependency>

源码分析

  • 数据发生变动ApplicationEvent 触发DataChanged事件

  • zk监听器接收到数据发生变化,就判断事件类型 ,如果是DELETE,还要删除对应的Selector,Rule数据,
    否则创建或更新节点。

    public ZookeeperDataChangedListener(final ZkClient zkClient) {
        this.zkClient = zkClient;
    }
    
    public void onPluginChanged(final List<PluginData> changed, final DataEventTypeEnum eventType) {
            for (PluginData data : changed) {
                final String pluginPath = ZkPathConstants.buildPluginPath(data.getName());
                // delete
                if (eventType == DataEventTypeEnum.DELETE) {
                    deleteZkPathRecursive(pluginPath);
                    final String selectorParentPath = ZkPathConstants.buildSelectorParentPath(data.getName());
                    deleteZkPathRecursive(selectorParentPath);
                    final String ruleParentPath = ZkPathConstants.buildRuleParentPath(data.getName());
                    deleteZkPathRecursive(ruleParentPath);
                    continue;
                }
                //create or update
                upsertZkNode(pluginPath, data);
            }
        }
    
  • zkclient客户端判断节点是否存在,不存在创建节点再写数据。

    private void upsertZkNode(final String path, final Object data) {
        if (!zkClient.exists(path)) {
            zkClient.createPersistent(path, true);
        }
        zkClient.writeData(path, data);
    }
    
  • soul-bootstrap 引入soul-spring-boot-starter-sync-data-zookeeper,里面的自动配置类 ZookeeperSyncDataConfiguration,创建了同步数据的syncDataService类,往spring容器注册了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));
    }
    
    @Bean
    public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) {
        return new ZkClient(zookeeperConfig.getUrl(), zookeeperConfig.getSessionTimeout(), zookeeperConfig.getConnectionTimeout());
    }
    

在这里插入图片描述

  • ZookeeperSyncDataService 类实例化一个新的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 watcherSelector(final String pluginName) {
            String selectorParentPath = ZkPathConstants.buildSelectorParentPath(pluginName);
            // 获取选择器列表,启动监听
            List<String> childrenList = zkClientGetChildren(selectorParentPath);
            if (CollectionUtils.isNotEmpty(childrenList)) {
                childrenList.forEach(children -> {
                    String realPath = buildRealPath(selectorParentPath, children);
                    cacheSelectorData(zkClient.readData(realPath));
                    subscribeSelectorDataChanges(realPath);
                });
            }
            subscribeChildChanges(ConfigGroupEnum.SELECTOR, selectorParentPath, childrenList);
        }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值