Introduction to the

When soul-admin is started, it synchronizes plugin, selector, rule, metaData and other gateway data to ZooKeeper. DataChangedEvent events will be published when the data changes. Listening events synchronize data to ZooKeeper.

Zookeeper synchronizes data to the gateway, including the following two parts:

  • Soul-admin Changes gateway data and tracks the data synchronization process.
  • Soul-bootstrap How to obtain ZooKeeper data and how to sense changes in gateway data.

Source code analysis

Soul-admin Changes gateway data and tracks the data synchronization process

1. Try to change the status of Divide plug-in in gateway background, debug trace, as follows:

2. A DataChangedEvent event will be published after the plugin is updated, as follows:

3, DataChangedEventDispatcher onApplicationEvent () is responsible for monitoring events, are as follows:

4, ZookeeperDataChangedListener responsible for synchronous data to the zookeeper, as follows:

Soul-bootstrap How to obtain zooKeeper data and how to sense changes in gateway data

<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>soul-spring-boot-starter-sync-data-zookeeper</artifactId>
    <version>${project.version}</version>
</dependency>
Copy the code

2, the soul – the bootstrap start automatically after injection ZookeeperSyncDataConfiguration for reading Zookeeper configuration to inject ZkClient container, SyncDataService added data synchronization service bean to the container, Get from The Spring container, ZkClient (ZooKeeper client), pluginSubscriber (plug-in data subscription), metaSubscribers (metadata subscription), authSubscribers (rights subscription), the code is as follows:

public class ZookeeperSyncDataConfiguration {
    /**
     * Sync data service sync data service.
     * @param zkClient          the zk client
     * @param pluginSubscriber the plugin subscriber
     * @param metaSubscribers   the meta subscribers
     * @param authSubscribers   the auth subscribers
     * @return the sync data service
     */
    @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));
    }
    /**
     * register zkClient in spring ioc.
     * @param zookeeperConfig the zookeeper configuration
     * @return ZkClient {@linkplain ZkClient}
     */
    @Bean
    public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) {
        return newZkClient(zookeeperConfig.getUrl(), zookeeperConfig.getSessionTimeout(), zookeeperConfig.getConnectionTimeout()); }}Copy the code

3. ZookeeperSyncDataService initialization, also known as soul-bootstrap startup, will obtain data from ZooKeeper and synchronize it to the memory, as follows:

  • WatcherData () –> watcherAll() –> watcherPlugin() –> cachePluginData().
  • ZkClient. SubscribeDataChanges () to monitor the current node and the content of the child nodes to modify, delete.

The code is as follows:

public class ZookeeperSyncDataService implements SyncDataService.AutoCloseable {
    private final ZkClient zkClient;
    private final PluginDataSubscriber pluginDataSubscriber;
    private final List<MetaDataSubscriber> metaDataSubscribers;
    private final List<AuthDataSubscriber> authDataSubscribers;
    /**
     * Instantiates a new Zookeeper cache manager.
     * @param zkClient             the zk client
     * @param pluginDataSubscriber the plugin data subscriber
     * @param metaDataSubscribers  the meta data subscribers
     * @param authDataSubscribers  the auth data subscribers
     */
    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(a) {
	    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); }}Copy the code

4. Break point tracking in the watcherData method, as shown below:

WatcherAll:

WatcherPlugin and cachePluginData

Create a breakpoint on the cacheRuleData method of the ZookeeperSyncDataService class, update the plugin rules, and check whether the breakpoint is entered.

private void cacheRuleData(final RuleData ruleData) {
    Optional.ofNullable(ruleData)
            .ifPresent(data -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onRuleSubscribe(data)));
}
Copy the code

[root@soul-admin] [root@soul-admin] [root@soul-admin] [root@soul-admin] [root@soul-admin]

3. Soul-bootstrap does receive updates to the plugin data. According to the “Synchronization principle of ZooKeeper” on the Soul website, the watch mechanism is mainly used. The subscribeDataChanges() method of the ZookeeperSyncDataService class listens for content modification and deletion of the current node and child nodes, as shown in the following code:

zkClient.subscribeChildChanges(groupParentPath, (parentPath, currentChildren) -> {
    if (CollectionUtils.isNotEmpty(currentChildren)) {
        List<String> addSubscribePath = addSubscribePath(childrenList, currentChildren);
        // Get the newly added node data and subscribe to that node
        addSubscribePath.stream().map(addPath -> {
            String realPath = buildRealPath(parentPath, addPath);
            cacheRuleData(zkClient.readData(realPath));
            return realPath;
        }).forEach(this::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); }}); }Copy the code

Conclusion:

If the soul-admin configuration data is changed, the process is as follows:

Soul – admin side:

  1. Released through PluginServiceImpl ApplicationEventPublisher DataChangedEvent events;
  2. DataChangedEventDispatcher through onApplicationEvent () is responsible for monitoring events;
  3. Data to the zookeeper ZookeeperDataChangedListener responsible for synchronization.

soul-bootstrap

  1. ZookeeperSyncDataService class by zkClient. SubscribeDataChanges () method to monitor the current node and the content of the child nodes to modify, delete;
  2. Call the cacheXxxData method of the ZookeeperSyncDataService class;
  3. Through step 2 cacheXxxData method call CommonPluginDataSubscriber onXxxSubscribe method in a class, Final call to CommonPluginDataSubscriber class subscribeDataHandler method to update the cache data.