Soul网关源码解析(七):Zookeeper同步数据

Soul网关源码解析(七):Zookeeper同步数据

Soul网关源码解析(七)Zookeeper同步数据数据同步配置启动时的同步admin的同步处理小结参考

通过上一节,我们知道了websocket数据同步的流程,本小节学习Zookeeper数据同步的流程。

Zookeeper同步数据

数据同步配置

Admin与Bootstrap的配置参数相同,如下所示,支持的参数有Zk的url,Session超时时间,连接超时时间,指定序列化器:

soul :
    sync:
        zookeeper:
             url: localhost:2181
             sessionTimeout: 5000
             connectionTimeout: 2000

程序如何知道启用zookeeper数据同步?这是因为在加载配置类时,下面这个配置类有个@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url"),如果配置属性中存在soul.sync.zookeeper.url,那么就会初始化这个配置类,而这个配置类又需要加载ZookeeperSyncDataService类,这就是zookeeper同步数据的核心类。

@Configuration
@ConditionalOnClass(ZookeeperSyncDataService.class)
@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
@EnableConfigurationProperties(ZookeeperConfig.class)
@Slf4j
public class ZookeeperSyncDataConfiguration {
  ...
}

Bootstrap启动时的同步

下面来看一下这个Bean构建时如何进行同步的,首先@Bean注释的方法,如果方法有参数,会先从Spring Bean容器里获取这些对象,如果没有,那么Spring就会进行构造,构造的前提是这些类有被spring注解标识(比如@Bean),希望由Spring容器管理对象生命周期,这里构建ZookeeperSyncDataService对象时,需要传入ZkClient对象,那就需要构建这个对象,构建ZkClient对象时,需要传入ZookeeperConfig对象,那Spring容器就会尝试构建这个对象,ZookeeperConfig对象会根据配置中前缀为soul.sync.zookeeper的值对其进行属性赋值并创建。

#ZookeeperSyncDataConfiguration
    @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());
    }
​
#ZookeeperConfig
@Data
@ConfigurationProperties(prefix = "soul.sync.zookeeper")
public class ZookeeperConfig {
​
    private String url;
​
    private Integer sessionTimeout;
​
    private Integer connectionTimeout;
​
    private String serializer;
}

ZookeeperSyncDataService对象的构建除了常规的设置成员变量,还通过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();
    }
    
    private void watcherData() {
        final String pluginParent = ZkPathConstants.PLUGIN_PARENT;
        // 从zk获取节点列表
        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 watcherAll(final String pluginName) {
        watcherPlugin(pluginName);
        watcherSelector(pluginName);
        watcherRule(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);
    }
​
    // 订阅插件数据
    private void cachePluginData(final PluginData pluginData) {
        Optional.ofNullable(pluginData).flatMap(data -> Optional.ofNullable(pluginDataSubscriber)).ifPresent(e -> e.onSubscribe(pluginData));
    }    
​
#CommonPluginDataSubscriber
    public void onSubscribe(final PluginData pluginData) {
        subscribeDataHandler(pluginData, DataEventTypeEnum.UPDATE);
    }
​
    private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
        Optional.ofNullable(classData).ifPresent(data -> {
            // 数据是否为插件数据
            if (data instanceof PluginData) {
                PluginData pluginData = (PluginData) data;
                // 如果操作是更新,则更新缓存
                if (dataType == DataEventTypeEnum.UPDATE) {
                    BaseDataCache.getInstance().cachePluginData(pluginData);
                    Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData));
                } else if (dataType == DataEventTypeEnum.DELETE) {
                    BaseDataCache.getInstance().removePluginData(pluginData);
                    // 尝试调用插件的处理器
                    Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.removePlugin(pluginData));
                }
            } else if (data instanceof SelectorData) {
                SelectorData selectorData = (SelectorData) data;
                if (dataType == DataEventTypeEnum.UPDATE) {
                    BaseDataCache.getInstance().cacheSelectData(selectorData);
                    Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData));
                } else if (dataType == DataEventTypeEnum.DELETE) {
                    BaseDataCache.getInstance().removeSelectData(selectorData);
                    Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.removeSelector(selectorData));
                }
            } else if (data instanceof RuleData) {
                RuleData ruleData = (RuleData) data;
                if (dataType == DataEventTypeEnum.UPDATE) {
                    BaseDataCache.getInstance().cacheRuleData(ruleData);
                    Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData));
                } else if (dataType == DataEventTypeEnum.DELETE) {
                    BaseDataCache.getInstance().removeRuleData(ruleData);
                    Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.removeRule(ruleData));
                }
            }
        });
    }
    
#ZookeeperSyncDataService
    private void subscribePluginDataChanges(final String pluginPath, final String pluginName) {
        zkClient.subscribeDataChanges(pluginPath, new IZkDataListener() {
​
            @Override
            public void handleDataChange(final String dataPath, final Object data) {
                Optional.ofNullable(data)
                        .ifPresent(d -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onSubscribe((PluginData) d)));
            }
​
            @Override
            public void handleDataDeleted(final String dataPath) {
                final PluginData data = new PluginData();
                data.setName(pluginName);
                Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.unSubscribe(data));
            }
        });
    }
​

admin的同步处理

这里DataChangedEventDispatcher实现了InitializingBean接口,会在admin启动初始化Bean时,执行如下函数,这个函数会获取所有DataChangedListener类的Bean对象,并存入List成员变量中。这里创建了Zk的监听器,并初始化了Zk节点数据,

#DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean
    @Override
    public void afterPropertiesSet() {
        Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
        this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
    }
    
#DataSyncConfiguration
    @Configuration
    @ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
    @Import(ZookeeperConfiguration.class)
    static class ZookeeperListener {
        @Bean
        @ConditionalOnMissingBean(ZookeeperDataChangedListener.class)
        public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) {
            return new ZookeeperDataChangedListener(zkClient);
        }
    
        @Bean
        @ConditionalOnMissingBean(ZookeeperDataInit.class)
        public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) {
            return new ZookeeperDataInit(zkClient, syncDataService);
        }
    }
    
#ZookeeperDataInit implements CommandLineRunner    
    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
    public boolean syncAll(final DataEventTypeEnum type) {
        // 认证数据同步
        appAuthService.syncData();
        List<PluginData> pluginDataList = pluginService.listAll();
        // 插件数据同步刷新,DataEventTypeEnum为REFRESH
        // 广播发布事件有变化,让各个监听器接收到数据变化,并作出各自的处理
        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;
    }
    
#DataChangedEventDispatcher   
    当时事件发生,根据事件Key,调用不同监听器进行事件变更处理
    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());
            }
        }
    }
    
#ZookeeperDataChangedListener
   public void onPluginChanged(final List<PluginData> changed, final DataEventTypeEnum eventType) {
        for (PluginData data : changed) {
            final String pluginPath = ZkPathConstants.buildPluginPath(data.getName());
            // delete 时,会将zk中的数据删除,包括插件的选择器和规则
            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);
        }
    }
    
    //真实写入zk
    private void upsertZkNode(final String path, final Object data) {
        if (!zkClient.exists(path)) {
            zkClient.createPersistent(path, true);
        }
        zkClient.writeData(path, data);
    }

小结

本小结以配置Zookeeper同步数据的方式入手,介绍了admin和bootstrap端配置要一致,接着走读了bootstrap启动时进行的zookeeper同步的流程,依据订阅zk节点的事件,当节点发生变化,做出相应的处理,最后介绍了一下admin这边启动时做的zookeeper数据同步的准备工作,并在发生变化时的处理流程,自此本小节结束。希望能帮到你,初识soul这样一个极致性能的网关项目。

参考

Soul 官网

猜你喜欢

转载自blog.csdn.net/S_powerStone/article/details/113133345