soul网关学习七之soul-admin使用zookeeper同步数据到网关

上一篇中介绍了使用websocket同步数据,soul也可以使用zk来同步数据

1.配置

引入 zk 相关的jar包
在这里插入图片描述
soul-admin中打开zk配置
在这里插入图片描述

soul-bootstrap中打开 zk配置
在这里插入图片描述
因为使用zk,需要单独启动一个zk。
引用 soul-spring-boot-starter-sync-data-zookeeper
在这里插入图片描述

2. 配置加载

2.1 soul-admin 端

admin端使用spring的事件注册机制在更改配置时发送事件,将修改的配置发送出去。接收事件后将获取的配置修改到zk的节点上。
下面看下源码

2.1.1 发布事件

每个service都会注入 eventPublisher 用来发布配置修改的事件 DataChangedEvent
在这里插入图片描述
在这里插入图片描述

2.1.2 接收事件

DataChangedEventDispatcher 用来接收 DataChangedEvent 事件,该类会加载容器内配置的 DataChangedListener
在这里插入图片描述

public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
    private ApplicationContext applicationContext;
    private List<DataChangedListener> listeners;
    public DataChangedEventDispatcher(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
//  接收 DataChangedEvent事件后使用循环DataChangedListener处理事件 
    @Override
    @SuppressWarnings("unchecked")
    public void onApplicationEvent(final DataChangedEvent event) {
        for (DataChangedListener listener : listeners) {
            switch (event.getGroupKey()) {
                case APP_AUTH:
                    listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());
                    break;
                case PLUGIN:
                    listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());
                    break;
                case RULE:
                    listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
                    break;
                case SELECTOR:
                    listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
                    break;
                case META_DATA:
                    listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
            }
        }
    }
    // 获取容器中的 DataChangedListener
    @Override
    public void afterPropertiesSet() {
        Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
        this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
    }
}

我们可以看到会根据不同的groupKey执行不同的方法,我们以 RULE 为例,即 ZookeeperDataChangedListener.onRuleChanged()

    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);
        }
    }

由于 soul-boostrap 端对zk节点进行监听,所以admin端对数据节点更新后soul-bootstrap会监听到改节点

2.1 soul-boostrap 配置加载及zk的监听

项目启动后会加载 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;
        watcherData();
        watchAppAuth();
        watchMetaData();
    }

在构造方法中设置了 DataSubscriber 用于操作元数据
并且在 watcherData watchAppAuth watchMetaData 中通过监听zk节点来修改数据。 watcherData 方法为例:

    private void watcherData() {
//        plugin 数据路径
        final String pluginParent = ZkPathConstants.PLUGIN_PARENT;
//        子节点路径
        List<String> pluginZKs = zkClientGetChildren(pluginParent);
        for (String pluginName : pluginZKs) {
//            监控子节点
            watcherAll(pluginName);
        }
//        监控plugin根节点
        zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> {
            if (CollectionUtils.isNotEmpty(currentChildren)) {
                for (String pluginName : currentChildren) {
                    watcherAll(pluginName);
                }
            }
        });
    }
    private void watcherAll(final String pluginName) {
//        监控 plugin
        watcherPlugin(pluginName);
//        监控 selector
        watcherSelector(pluginName);
//        监控 rule
        watcherRule(pluginName);
    }

watcherRule 方法

    private void watcherRule(final String pluginName) {
//        rule parent节点路径
        String ruleParent = ZkPathConstants.buildRuleParentPath(pluginName);
//        子节点路径
        List<String> childrenList = zkClientGetChildren(ruleParent);
//        处理子节点数据
        if (CollectionUtils.isNotEmpty(childrenList)) {
            childrenList.forEach(children -> {
                String realPath = buildRealPath(ruleParent, children);
//                使用不同的 DataSubscriber 缓存zk数据到本地
                cacheRuleData(zkClient.readData(realPath));
//              订阅rule数据节点的变化
                subscribeRuleDataChanges(realPath);
            });
        }
//        处理子节点下的节点数据变化
        subscribeChildChanges(ConfigGroupEnum.RULE, ruleParent, childrenList);
    }

subscribeRuleDataChanges 方法 处理当前节点

private void subscribeRuleDataChanges(final String path) {
        zkClient.subscribeDataChanges(path, new IZkDataListener() {
//            订阅更新节点事件
            @Override
            public void handleDataChange(final String dataPath, final Object data) {
//                缓存本地
                cacheRuleData((RuleData) data);
            }
//          订阅节点删除事件
            @Override
            public void handleDataDeleted(final String dataPath) {
//                删除本地缓存
                unCacheRuleData(dataPath);
            }
        });
    }

subscribeChildChanges 处理子节点

private void subscribeChildChanges(final ConfigGroupEnum groupKey, final String groupParentPath, final List<String> childrenList) {
        switch (groupKey) {
            case SELECTOR:
//                订阅 节点更新事件
                zkClient.subscribeChildChanges(groupParentPath, (parentPath, currentChildren) -> {
                    if (CollectionUtils.isNotEmpty(currentChildren)) {
//                        这里判断当前更新的节点是不是在传进来的节点集合里,及更新的节点是否为想要监控的节点
                        List<String> addSubscribePath = addSubscribePath(childrenList, currentChildren);
//                        本地缓存节点数据
                        addSubscribePath.stream().map(addPath -> {
                            String realPath = buildRealPath(parentPath, addPath);
                            cacheSelectorData(zkClient.readData(realPath));
                            return realPath;
                        }).forEach(this::subscribeSelectorDataChanges); // 继续监听节点(zk监听一次后会取消监听,所以需要再次设置监听)
                    }
                });
                break;
// 其他省略

以上就是使用zookeeper进行同步数据的流程,如果那里有错误请指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值