A list,

Soul uses the ZooKeeper approach to synchronizing data from HTTP long polling.

Second, run the example

1. Modify the soul-admin configuration file applica. yml to enable zooKeeper data synchronization as follows:

soul:
  sync:
      zookeeper:
          url: localhost:2181
          sessionTimeout: 5000
          connectionTimeout: 2000
Copy the code

2, Modify the soul-bootstrap configuration file pom. XML and add the following configuration:

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

Application-local. yml, enable the ZooKeeper data synchronization switch as follows:

soul :
    sync:
        zookeeper:
             url: localhost:2181
             sessionTimeout: 5000
             connectionTimeout: 2000
Copy the code

3. Run zkserver. CMD in D:\Software\apache-zookeeper-3.6.2-bin\bin

4. The soul-admin gateway background service is started. After the service is started, ZooKeeper requests are displayed

The 2021-01-30 01:22:33. 3076-510 the INFO [- localhost: 2181] org. I0Itec. Zkclient. ZkEventThread: Starting ZkClient event thread. 2021-01-30 01:22:42. 593 INFO 3076 - [the main] org. Apache. The zookeeper. ClientCnxn: Zookeeper. Request. The timeout value is 0. Feature enabled = 2021-01-30 01:22:42. 3076-593 the INFO [main] org.I0Itec.zkclient.ZkClient : Waiting for Keeper state SyncConnected 2021-01-30 01:22:42.601 INFO 3076 -- [localhost:2181] org.apache.zookeeper.ClientCnxn : Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown Error) 01:22:42 2021-01-30. 3076-603 the INFO [localhost: 2181)] org. Apache. The zookeeper. ClientCnxn: Socket connection established, initiating session, client: /0:0:0:0:0:0:0:1:52208, server: Localhost / 0:0:0:0:0:0:0:1:21 81 01:22:42 2021-01-30. 3076-641 the INFO [localhost: 2181)] org. Apache. The zookeeper. ClientCnxn:  Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x100001059be0001, Negotiated timeout = 5000 2021-01-30 01:22:42. 3076-644 the INFO/ain - EventThread org. I0Itec. Zkclient. Zkclient: zookeeper state changed (SyncConnected)Copy the code

5. The soul-Bootstrap gateway service is started. After the service is started, the ZooKeeper request is invoked

The 2021-01-30 01:33:31. 15332-070 the INFO [the main] S.B.S.D.Z.Z ookeeperSyncDataConfiguration: you use zookeeper sync soul data....... The 2021-01-30 01:33:31. 15332-082 the INFO [- localhost: 2181] org. I0Itec. Zkclient. ZkEventThread: Starting ZkClient event thread. 2021-01-30 01:33:40. 148 INFO 15332 - [the main] org. Apache. The zookeeper. ClientCnxn: Zookeeper. Request. The timeout value is 0. Feature enabled = 2021-01-30 01:33:40. 15332-148 the INFO [main] org.I0Itec.zkclient.ZkClient : Waiting for Keeper state SyncConnected 2021-01-30 01:33:40.156 INFO 15332 -- [localhost:2181] org.apache.zookeeper.ClientCnxn : Opening a socket connection to the server localhost / 127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown Error) 01:33:40 2021-01-30. 15332-158 the INFO [localhost: 2181)] org. Apache. The zookeeper. ClientCnxn: Socket connection established, initiating session, client: /127.0.0.1:52634, server: Localhost / 127.0.0.1:2181 2021-01-30 01:33:40. 15332-199 the INFO [localhost: 2181)] org. Apache. The zookeeper. ClientCnxn: The Session establishment complete on server localhost / 127.0.0.1:2181, sessionid = 0 x100001059be0002, Negotiated timeout = 5000 2021-01-30 01:33:40. 15332-206 the INFO/ain - EventThread org. I0Itec. Zkclient. Zkclient: zookeeper state changed (SyncConnected)Copy the code

6. Start the ZooKeeper client ZooInspector and check the synchronized registration information of the Soul gateway on ZooKeeper.

Third, source code analysis

After the soul – admin start org. In the console I0Itec. Zkclient. Zkclient, tracking and debugging for entry.

ZookeeperConfiguration registers zkClient with the Spring container as follows:

/ / EnableConfigurationProperties role: use @ ConfigurationProperties annotation class to take effect. If a configuration class configates only the @ConfigurationProperties annotation and does not use @Component, then the beans transformed by the Properties configuration file are not available in the IOC container. @ EnableConfigurationProperties is equivalent to the use of the @ ConfigurationProperties class conducted an injection.
ConditionalOnMissingBean ConditionalOnMissingBean ConditionalOnMissingBean ConditionalOnMissingBean ConditionalOnMissingBean
/**
 * ZookeeperConfiguration .
 * @author xiaoyu(Myth)
 */
@EnableConfigurationProperties(ZookeeperProperties.class)
public class ZookeeperConfiguration {
    /**
     * register zkClient in spring ioc.
     *
     * @param zookeeperProp the zookeeper configuration
     * @return ZkClient {@linkplain ZkClient}
     */
    @Bean
    @ConditionalOnMissingBean(ZkClient.class)
    public ZkClient zkClient(final ZookeeperProperties zookeeperProp) {
        return newZkClient(zookeeperProp.getUrl(), zookeeperProp.getSessionTimeout(), zookeeperProp.getConnectionTimeout()); }}Copy the code

Soul-admin reads zooKeeper configuration information and injects zkClient into the container to establish a connection with ZooKeeper.

2, instantiation ZkClient call stack will call DataChangedEventDispatcher afterPropertiesSet method.

  • Org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher: Event repeater, forwarded to each ConfigEventListener of events that will change, such implementation InitializingBean, in the process of DataChangedEventDispatcher initialization, executes the afterPropertiesSet method;
  • AfterPropertiesSet method would be to look for in the container type is DataChangedListener bean class, as shown below:
@Component
public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
    private ApplicationContext applicationContext;
    private List<DataChangedListener> listeners;
    public DataChangedEventDispatcher(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
@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; .default:
                throw new IllegalStateException("Unexpected value: "+ event.getGroupKey()); }}...@Override
    public void afterPropertiesSet(a) {
        Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
        this.listeners = Collections.unmodifiableList(newArrayList<>(listenerBeans)); }}Copy the code

3, the afterPropertiesSet method execution will find DataChangedListener. The class instantiation of related classes.

  • Org. Dromara. Soul. Admin. Config. DataSyncConfiguration: data synchronization configuration class.

  • ZookeeperDataChangedListener: data change listeners, listening metadata changes, and then synchronization to zookeeper.

  • ZookeeperDataInit: synchronizes initial data to ZooKeeper.

/** * The type Zookeeper listener. */
@Configuration
@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
@Import(ZookeeperConfiguration.class)
static class ZookeeperListener {
    /**
     * Config event listener data changed listener.
     * @param zkClient the zk client
     * @return the data changed listener
     */
    @Bean
    @ConditionalOnMissingBean(ZookeeperDataChangedListener.class)
    public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) {
        return new ZookeeperDataChangedListener(zkClient);
    }
    /**
     * Zookeeper data init zookeeper data init
     * @param zkClient        the zk client
     * @param syncDataService the sync data service
     * @return the zookeeper data init
     */
    @Bean
    @ConditionalOnMissingBean(ZookeeperDataInit.class)
    public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) {
        return newZookeeperDataInit(zkClient, syncDataService); }}Copy the code

4, org. Dromara. Soul. Admin. The listener. The zookeeper. ZookeeperDataInit: responsible for the initialization zookeeper synchronous data. This class implements CommandLineRunner.

  • CommandLineRunner: SpringBoot iterates through all the entity classes that implement CommandLineRunner after a project is launched and executes the Run method. If you need to execute in a certain Order, you need to use an @ORDER annotation on the entity class (or implement the Order interface) to indicate that Order.

  • The run method calls the syncDataService. SyncAll method.

public class ZookeeperDataInit implements CommandLineRunner {
    private final ZkClient zkClient;
    private final SyncDataService syncDataService;
    /**
     * Instantiates a new Zookeeper data init.
     * @param zkClient        the zk client
     * @param syncDataService the sync data service
     */
    public ZookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) {
        this.zkClient = zkClient;
        this.syncDataService = syncDataService;
    }
    @Override
    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); }}}Copy the code

5, org. Dromara. Soul. Admin. Service. Sync. SyncDataServiceImpl

  • SyncAll method will be called the publishing of events and event type is DataEventTypeEnum REFRESH
/**
 * The type sync data service.
 * @author xiaoyu(Myth)
 */
@Service("syncDataService")
public class SyncDataServiceImpl implements SyncDataService {
    // Publish events, that is, all the listeners related to an event that are told about it
    private finalApplicationEventPublisher eventPublisher; .@Override
    public boolean syncAll(final DataEventTypeEnum type) {
        appAuthService.syncData();
        List<PluginData> pluginDataList = pluginService.listAll();
        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; }... }Copy the code

6, events, after the release of org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher onApplicationEvent method will listen for an event class changes, iterate through all the listeners data synchronization processing, The listener is ZookeeperDataChangedListener implementation class, depending on the type of event corresponding to the zookeeper through zkClient synchronous data.

@Component
public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
@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()); }}}}Copy the code

Four,

Soul-admin synchronizes gateway data such as rule, metaData, selector, plugin, and ZooKeeper. A DataChangedEvent event is published for data changes. The listening event synchronizes data to ZooKeeper.