Soul 学习笔记---使用 zookeeper 实现数据同步(六)

今天看下 zookeeper 怎么实现数据同步的。

首先查下 zookeeper 是什么,网上看了一些资料,大概明白了它的用处。

分布式系统有个 CAP 理论:

  • 一致性:在分布式环境中,一致性是指数据在多个副本之间是否能够保持一致的特性,等同于所有节点访问同一份最新的数据副本。在一致性的需求下,当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一致的状态。
  • 可用性:每次请求都能获取到正确的响应,但是不保证获取的数据为最新数据。
  • 分区容错性:分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。

一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。分布式系统中,P 是必须的,因此只能在 CP 和 AP 中选择,zookeeper 保证的是 CP,对比 spring cloud 系统中的注册中心 eruka 实现的是 AP。

zookeeper 保证了分布式环境下数据的一致性。我觉得 zookeeperredis 很像,虽然底层实现机制不一样,但他们主要就是为了存储数据,不过 zookeeper 有一个 watch 机制,类似于观察者模式,watch 流程是客户端向服务端某个节点路径上注册一个 watcher,同时客户端也会存储特定的 watcher,当节点数据或子节点发生变化时,服务端通知客户端,客户端进行回调处理。soul 网关使用 zookeeper 进行数据同步,就利用的这个机制。

soul-adminsoul-bootstrap 项目的 application-local.yml 文件设置使用 zookeeper 进行同步。

soul:
  sync:
#    websocket:
#        enabled : true
    zookeeper:
        url: localhost:2181
        sessionTimeout: 5000
        connectionTimeout: 2000

启动 soul-admin 时,会返回一个ZookeeperDataChangedListenerZookeeperDataInit 实例。

ZookeeperDataInit 就是在启动时,把 selectorrule 等数据写入 zookeeper,每当有数据变更,就会更新 zookeeper 节点。

soul-bootstrap 启动时,会去加载类 ZookeeperSyncDataConfiguration ,它会新建一个 ZookeeperSyncDataService 实例。

    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;
        //监听 plugin,selector,rule
        watcherData();
        watchAppAuth();
        watchMetaData();
    }

    private void watcherData() {
        final String pluginParent = ZkPathConstants.PLUGIN_PARENT;
        if (!zkClient.exists(pluginParent)) {
            //节点不存在,将数据写入节点
            zkClient.createPersistent(pluginParent, true);
        }
        List<String> pluginZKs = zkClient.getChildren(ZkPathConstants.buildPluginParentPath());
        for (String pluginName : pluginZKs) {
            watcherAll(pluginName);
        }
        //这里订阅了节点的变化。
        zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> {
            if (CollectionUtils.isNotEmpty(currentChildren)) {
                for (String pluginName : currentChildren) {
                    watcherAll(pluginName);
                }
            }
        });
    }

可以看到这个类会监听 selectorrule 等数据,如果它们在 soul-admin 发生变化,这里就能接收到,从而更新内存中的数据。

参考资料

zookeeper 教程

原文地址:https://www.cnblogs.com/fightingting/p/14305944.html