Soul网关源码学习【第七篇】-数据同步(zookeeper)

关于zookeeper

可能有些同学跟我一样,没有用过zookeeper,不过这并没有多大影响来阅读本篇文章。关于zookeeper我们只需要知道它可以存储数据,并且可以广播数据就可以了。如果对zookeeper不是很了解并且感兴趣的话,推荐菜鸟教程的zookeeper教程适合初学者快速了解zookeeper。

Soul网关使用Zookeeper实现数据同步

前面一篇介绍WebSocket数据同步的时候已经大致说过了Soul的数据同步原理,今天就直接来演示了。

  • 启动soul-admin

  • 启动soul-bootstrap
    这里需要注意的是,Soul默认使用WebSocket进行数据同步,如果要使用zookeeper,需要先配置:

    • 首先在 pom.xml 文件中 引入以下依赖:
       	 <!--soul data sync start use zookeeper-->
           <dependency>
                <groupId>org.dromara</groupId>
                 <artifactId>soul-spring-boot-starter-sync-data-zookeeper</artifactId>
                 <version>${last.version}</version>
           </dependency>
      
    • 在 springboot的 yml 文件中进行如下配置:
      soul :
           sync:
               zookeeper:
                    url: localhost:2181
                    sessionTimeout: 5000
                    connectionTimeout: 2000
       #url: 配置成你的zk地址,集群环境请使用(,)分隔
      
    • soul-admin 配置, 或在 soul-admin 启动参数中设置 --soul.sync.zookeeper.url=‘你的地址’,然后重启服务。
      soul:
        sync:
          zookeeper:
              url: localhost:2181
              sessionTimeout: 5000
              connectionTimeout: 2000
      
  • 启动完成后,我们可以在zookeeper的client端看到如下的数据节点,多了五个节点,通过名字就知道这里面是存储我们的插件/选择器/规则/以及元数据的。并且客户端出现如下log信息,开始监视我们/soul下面的数据。
    WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/soul
    在这里插入图片描述

  • 现在我们看一下soul-admin数据同步是调用的哪个方法

    • 在浏览器的控制台可以看到我们点数据同步按钮时,调用的下面这个方法:
      在这里插入图片描述
        /**
           * Sync plugin data.
           *
           * @param id the id
           * @return the mono
           */
          @PutMapping("/syncPluginData/{id}")
          public SoulAdminResult syncPluginData(@PathVariable("id") final String id) {
              boolean success = syncDataService.syncPluginData(id);
              if (success) {
                  return SoulAdminResult.success(SoulResultMessage.SYNC_SUCCESS);
              } else {
                  return SoulAdminResult.error(SoulResultMessage.SYNC_FAIL);
              }
          }
      
    • 跟着断点一步步走,会看到进入到下面这个方法,这里做的处理就是根据插件Id找到插件,然后把插件信息发送出去,如果这个插件下面有选择器的话,也会把选择器的信息发送出去。(这里的eventPublisherorg.springframework.context.ApplicationEventPublisher,这个是Spring的一个事件发布器,它的相关介绍可以参照这篇文章,我这里就不详细介绍了。)
    • 还可以发现这里发送出去的是一个DataChangedEvent,这里面就是我们前一篇介绍的数据同步的时候需要的几个参数,(插件类型/操作类型/以及json数据)
      @Override
          public boolean syncPluginData(final String pluginId) {
              PluginVO pluginVO = pluginService.findById(pluginId);
              eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, DataEventTypeEnum.UPDATE,
                      Collections.singletonList(PluginTransfer.INSTANCE.mapDataTOVO(pluginVO))));
              List<SelectorData> selectorDataList = selectorService.findByPluginId(pluginId);
              if (CollectionUtils.isNotEmpty(selectorDataList)) {
                  eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.REFRESH, selectorDataList));
                  List<RuleData> allRuleDataList = new ArrayList<>();
                  for (SelectorData selectData : selectorDataList) {
                      List<RuleData> ruleDataList = ruleService.findBySelectorId(selectData.getId());
                      allRuleDataList.addAll(ruleDataList);
                  }
                  eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, DataEventTypeEnum.REFRESH, allRuleDataList));
              }
              return true;
          }
      
  • 通过事件发送器发送给zookeeper后,我们admin的操作就完成了,下面再看一下我们网关是怎么接收数据的。

  • 找到数据同步模块下的关于zookeeper的部分soul-sync-data-zookeeper,看到里面只有一个ZookeeperSyncDataService,(不由心里一阵欢喜,好简单。。)

  • 可以看到这个类里面好多watcher***的方法,猜测就是当zookeeper数据发生变化是,会触发这里,试验一下。

    • 先给watcherPlugin打个断点,然后在画面上点数据同步按钮,它真的就进来了。而通过调用栈看出watcherData方法就是入口。
      在这里插入图片描述
  • 我们从watcherData方法开始,结合watcherPlugin看一下,可以看出我们这里就是从zk中拿到数据,更新到我们的缓存中。(更新缓存这块在昨天的分享中已经介绍过了,这里是通用的方法,就不再赘述了。)

    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);
                    }
                }
            });
        }
    
    	private void watcherPlugin(final String pluginName) {
            String pluginPath = ZkPathConstants.buildPluginPath(pluginName);
            if (!zkClient.exists(pluginPath)) {
                zkClient.createPersistent(pluginPath, true);
            }
            cachePluginData(zkClient.readData(pluginPath));
            subscribePluginDataChanges(pluginPath, pluginName);
        }
    

小结

今天跟着debug走了一下zookeeper数据同步的一个流程,大体一个流程是走完了,只是涉及zookeeper的一些机制我还不是太懂,最好是先了解一下zookeeper的使用方法,再看整个流程会更清晰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值