This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new 64b24f7ddb Use listening mode for Apollo configuration. (#11186)
64b24f7ddb is described below

commit 64b24f7ddb45345da8ad5f9ac1a84394122ad5ff
Author: weixiang1862 <[email protected]>
AuthorDate: Tue Aug 8 20:17:18 2023 +0800

    Use listening mode for Apollo configuration. (#11186)
---
 docs/en/changes/changes.md                         |   3 +-
 docs/en/setup/backend/configuration-vocabulary.md  |   1 -
 docs/en/setup/backend/dynamic-config-apollo.md     |   1 -
 .../trace/TraceSamplingPolicyWatcherTest.java      |   7 +-
 .../configuration/api/ConfigWatcherRegister.java   | 270 +++++----------------
 ...ter.java => FetchingConfigWatcherRegister.java} | 100 +-------
 .../api/ListeningConfigWatcherRegister.java        |  60 +++++
 ...java => FetchingConfigWatcherRegisterTest.java} |  14 +-
 .../api/ListeningConfigWatcherRegisterTest.java    | 178 ++++++++++++++
 .../apollo/ApolloConfigWatcherRegister.java        |  87 ++++---
 .../consul/ConsulConfigurationWatcherRegister.java |   4 +-
 .../etcd/EtcdConfigWatcherRegister.java            |   6 +-
 .../ConfigmapConfigurationWatcherRegister.java     |   4 +-
 .../nacos/NacosConfigWatcherRegister.java          |   7 +-
 .../zookeeper/ZookeeperConfigWatcherRegister.java  |   4 +-
 .../ut/MockZookeeperConfigWatcherRegister.java     |   4 +-
 .../grpc/GRPCConfigWatcherRegister.java            |   4 +-
 .../core/analysis/ApdexThresholdConfigTest.java    |   3 +-
 .../src/main/resources/application.yml             |   1 -
 19 files changed, 398 insertions(+), 360 deletions(-)

diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 5db6b4e537..dd3f997d6d 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -65,7 +65,8 @@
 * Remove ElasticSearch 6.3.2 from our client lib tests.
 * Bump up ElasticSearch server 8.8.1 to 8.9.0 for latest e2e testing. 8.1.0, 
7.16.3 and 7.17.10 are still tested.
 * Add OpenSearch 2.8.0 to our client lib tests.
-* Apply MQE on RabbitMQ Dashboards
+* Apply MQE on RabbitMQ Dashboards.
+* Use listening mode for apollo implementation of dynamic configuration.
 
 
 #### UI
diff --git a/docs/en/setup/backend/configuration-vocabulary.md 
b/docs/en/setup/backend/configuration-vocabulary.md
index b251db6994..c125103eb7 100644
--- a/docs/en/setup/backend/configuration-vocabulary.md
+++ b/docs/en/setup/backend/configuration-vocabulary.md
@@ -273,7 +273,6 @@ The Configuration Vocabulary lists all available 
configurations provided by `app
 | -                       | -             | apolloCluster                      
                                                                                
                                                      | `apollo.cluster` in 
Apollo.                                                                         
                                                                                
                                                                                
                  [...]
 | -                       | -             | apolloEnv                          
                                                                                
                                                      | `env` in Apollo.        
                                                                                
                                                                                
                                                                                
              [...]
 | -                       | -             | appId                              
                                                                                
                                                      | `app.id` in Apollo.     
                                                                                
                                                                                
                                                                                
              [...]
-| -                       | -             | period                             
                                                                                
                                                      | The period of data sync 
(in seconds).                                                                   
                                                                                
                                                                                
              [...]
 | -                       | zookeeper     | namespace                          
                                                                                
                                                      | The namespace 
(represented by root path) that isolates the configurations in the Zookeeper.   
                                                                                
                                                                                
                        [...]
 | -                       | -             | hostPort                           
                                                                                
                                                      | Hosts and ports of 
Zookeeper Cluster.                                                              
                                                                                
                                                                                
                   [...]
 | -                       | -             | baseSleepTimeMs                    
                                                                                
                                                      | The period of Zookeeper 
client between two retries (in milliseconds).                                   
                                                                                
                                                                                
              [...]
diff --git a/docs/en/setup/backend/dynamic-config-apollo.md 
b/docs/en/setup/backend/dynamic-config-apollo.md
index 4f937b3daa..be206fb872 100755
--- a/docs/en/setup/backend/dynamic-config-apollo.md
+++ b/docs/en/setup/backend/dynamic-config-apollo.md
@@ -10,7 +10,6 @@ configuration:
     apolloCluster: ${SW_CONFIG_APOLLO_CLUSTER:default}
     apolloEnv: ${SW_CONFIG_APOLLO_ENV:""}
     appId: ${SW_CONFIG_APOLLO_APP_ID:skywalking}
-    period: ${SW_CONFIG_APOLLO_PERIOD:60}
 ```
 
 ## Config Storage
diff --git 
a/oap-server/analyzer/agent-analyzer/src/test/java/org/apache/skywalking/oap/server/analyzer/provider/trace/TraceSamplingPolicyWatcherTest.java
 
b/oap-server/analyzer/agent-analyzer/src/test/java/org/apache/skywalking/oap/server/analyzer/provider/trace/TraceSamplingPolicyWatcherTest.java
index 074af0856e..445aa6e1a2 100644
--- 
a/oap-server/analyzer/agent-analyzer/src/test/java/org/apache/skywalking/oap/server/analyzer/provider/trace/TraceSamplingPolicyWatcherTest.java
+++ 
b/oap-server/analyzer/agent-analyzer/src/test/java/org/apache/skywalking/oap/server/analyzer/provider/trace/TraceSamplingPolicyWatcherTest.java
@@ -25,6 +25,7 @@ import 
org.apache.skywalking.oap.server.analyzer.provider.trace.sampling.Samplin
 import org.apache.skywalking.oap.server.configuration.api.ConfigChangeWatcher;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
 import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -121,7 +122,7 @@ public class TraceSamplingPolicyWatcherTest {
             "  duration: 800");
     }
 
-    public static class TraceLatencyThresholdMockConfigWatcherRegister extends 
ConfigWatcherRegister {
+    public static class TraceLatencyThresholdMockConfigWatcherRegister extends 
FetchingConfigWatcherRegister {
 
         public TraceLatencyThresholdMockConfigWatcherRegister(long syncPeriod) 
{
             super(syncPeriod);
@@ -205,7 +206,7 @@ public class TraceSamplingPolicyWatcherTest {
             "  rate: 500");
     }
 
-    public static class DefaultSampleRateMockConfigWatcherRegister extends 
ConfigWatcherRegister {
+    public static class DefaultSampleRateMockConfigWatcherRegister extends 
FetchingConfigWatcherRegister {
 
         public DefaultSampleRateMockConfigWatcherRegister(long syncPeriod) {
             super(syncPeriod);
@@ -335,7 +336,7 @@ public class TraceSamplingPolicyWatcherTest {
 
     }
 
-    public static class ServiceMockConfigWatcherRegister extends 
ConfigWatcherRegister {
+    public static class ServiceMockConfigWatcherRegister extends 
FetchingConfigWatcherRegister {
 
         public ServiceMockConfigWatcherRegister(long syncPeriod) {
             super(syncPeriod);
diff --git 
a/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegister.java
index b4c6d23d33..11e7748d11 100644
--- 
a/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegister.java
@@ -18,240 +18,88 @@
 
 package org.apache.skywalking.oap.server.configuration.api;
 
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import 
org.apache.skywalking.oap.server.library.util.RunnableWithExceptionProtection;
-
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
 
 /**
  * The default implementor of Config Watcher register.
  */
 @Slf4j
 public abstract class ConfigWatcherRegister implements 
DynamicConfigurationService {
-    public static final String LINE_SEPARATOR = 
System.getProperty("line.separator", "\n");
-    private final Register singleConfigChangeWatcherRegister = new Register();
-    @Getter
-    private final Register groupConfigChangeWatcherRegister = new Register();
-    private volatile boolean isStarted = false;
-    private final long syncPeriod;
-
-    public ConfigWatcherRegister() {
-        this(60);
-    }
-
-    public ConfigWatcherRegister(long syncPeriod) {
-        this.syncPeriod = syncPeriod;
-    }
-
-    @Override
-    synchronized public void registerConfigChangeWatcher(ConfigChangeWatcher 
watcher) {
-        if (isStarted) {
-            throw new IllegalStateException("Config Register has been started. 
Can't register new watcher.");
-        }
 
-        WatcherHolder holder = new WatcherHolder(watcher);
-        if (singleConfigChangeWatcherRegister.containsKey(
-            holder.getKey()) || 
groupConfigChangeWatcherRegister.containsKey(holder.getKey())) {
-            throw new IllegalStateException("Duplicate register, watcher=" + 
watcher);
-        }
-
-        switch (holder.getWatcher().getWatchType()) {
-            case SINGLE:
-                singleConfigChangeWatcherRegister.put(holder.getKey(), holder);
-                break;
-            case GROUP:
-                groupConfigChangeWatcherRegister.put(holder.getKey(), holder);
-                break;
-            default:
-                throw new IllegalArgumentException(
-                    "Unexpected watch type of ConfigChangeWatcher " + 
watcher.toString());
+    public abstract void start();
+
+    protected void notifySingleValue(final ConfigChangeWatcher watcher, 
ConfigTable.ConfigItem configItem) {
+        String newItemValue = configItem.getValue();
+        if (newItemValue == null) {
+            if (watcher.value() != null) {
+                // Notify watcher, the new value is null with delete event 
type.
+                watcher.notify(
+                    new ConfigChangeWatcher.ConfigChangeEvent(null, 
ConfigChangeWatcher.EventType.DELETE));
+            } else {
+                // Don't need to notify, stay in null.
+            }
+        } else {
+            if (!newItemValue.equals(watcher.value())) {
+                watcher.notify(new ConfigChangeWatcher.ConfigChangeEvent(
+                    newItemValue,
+                    ConfigChangeWatcher.EventType.MODIFY
+                ));
+            } else {
+                // Don't need to notify, stay in the same config value.
+            }
         }
     }
 
-    public void start() {
-        isStarted = true;
-
-        log.info("Current configurations after the bootstrap sync." + 
LINE_SEPARATOR + singleConfigChangeWatcherRegister.toString());
-
-        Executors.newSingleThreadScheduledExecutor()
-                 .scheduleAtFixedRate(
-                     new RunnableWithExceptionProtection(
-                         this::configSync,
-                         t -> log.error("Sync config center error.", t)
-                     ), 0, syncPeriod, TimeUnit.SECONDS);
-    }
-
-    void configSync() {
-        singleConfigsSync();
-        groupConfigsSync();
-    }
-
-    private void singleConfigsSync() {
-        Optional<ConfigTable> configTable = 
readConfig(singleConfigChangeWatcherRegister.keys());
+    protected void notifyGroupValues(final GroupConfigChangeWatcher watcher,
+                                     final GroupConfigTable.GroupConfigItems 
groupConfigItems) {
+        Map<String, ConfigTable.ConfigItem> groupItems = 
groupConfigItems.getItems();
+        Map<String, ConfigChangeWatcher.ConfigChangeEvent> changedGroupItems = 
new HashMap<>();
+        Map<String, String> currentGroupItems = 
Optional.ofNullable(watcher.groupItems())
+                                                        .orElse(new 
HashMap<>());
+
+        groupItems.forEach((groupItemName, groupItem) -> {
+            String newItemValue = groupItem.getValue();
+            if (newItemValue == null) {
+                if (currentGroupItems.get(groupItemName) != null) {
+                    // Notify watcher, the new value is null with delete event 
type.
+                    changedGroupItems.put(groupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
+                        null,
+                        ConfigChangeWatcher.EventType.DELETE
+                    ));
 
-        // Config table would be null if no change detected from the 
implementation.
-        configTable.ifPresent(config -> {
-            config.getItems().forEach(item -> {
-                String itemName = item.getName();
-                WatcherHolder holder = 
singleConfigChangeWatcherRegister.get(itemName);
-                if (holder == null) {
-                    log.warn(
-                        "Config {} from configuration center, doesn't match 
any WatchType.SINGLE watcher, ignore.",
-                        itemName
-                    );
-                    return;
+                } else {
+                    // Don't need to notify, stay in null.
                 }
-                ConfigChangeWatcher watcher = holder.getWatcher();
-                String newItemValue = item.getValue();
-                if (newItemValue == null) {
-                    if (watcher.value() != null) {
-                        // Notify watcher, the new value is null with delete 
event type.
-                        watcher.notify(
-                            new ConfigChangeWatcher.ConfigChangeEvent(null, 
ConfigChangeWatcher.EventType.DELETE));
-                    } else {
-                        // Don't need to notify, stay in null.
-                    }
+            } else { //add and modify
+                if 
(!newItemValue.equals(currentGroupItems.get(groupItemName))) {
+                    changedGroupItems.put(groupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
+                        newItemValue,
+                        ConfigChangeWatcher.EventType.MODIFY
+                    ));
+
                 } else {
-                    if (!newItemValue.equals(watcher.value())) {
-                        watcher.notify(new 
ConfigChangeWatcher.ConfigChangeEvent(
-                            newItemValue,
-                            ConfigChangeWatcher.EventType.MODIFY
-                        ));
-                    } else {
-                        // Don't need to notify, stay in the same config value.
-                    }
+                    // Don't need to notify, stay in the same config value.
                 }
-            });
-            if (log.isTraceEnabled()) {
-                log.trace(
-                    "Current configurations after the sync." + LINE_SEPARATOR 
+ singleConfigChangeWatcherRegister.toString());
             }
         });
-    }
-
-    private void groupConfigsSync() {
-        Optional<GroupConfigTable> groupConfigTable = 
readGroupConfig(groupConfigChangeWatcherRegister.keys());
-        // Config table would be null if no change detected from the 
implementation.
-        groupConfigTable.ifPresent(config -> {
-            config.getGroupItems().forEach(groupConfigItems -> {
-                String groupConfigItemName = groupConfigItems.getName();
-                WatcherHolder holder = 
groupConfigChangeWatcherRegister.get(groupConfigItemName);
-
-                if (holder == null) {
-                    log.warn(
-                        "Config {} from configuration center, doesn't match 
any WatchType.GROUP watcher, ignore.",
-                        groupConfigItemName
-                    );
-                    return;
-                }
-
-                GroupConfigChangeWatcher watcher = (GroupConfigChangeWatcher) 
holder.getWatcher();
-                Map<String, ConfigTable.ConfigItem> groupItems = 
groupConfigItems.getItems();
-                Map<String, ConfigChangeWatcher.ConfigChangeEvent> 
changedGroupItems = new HashMap<>();
-                Map<String, String> currentGroupItems = 
Optional.ofNullable(watcher.groupItems())
-                                                                .orElse(new 
HashMap<>());
-
-                groupItems.forEach((groupItemName, groupItem) -> {
-                    String newItemValue = groupItem.getValue();
-                    if (newItemValue == null) {
-                        if (currentGroupItems.get(groupItemName) != null) {
-                            // Notify watcher, the new value is null with 
delete event type.
-                            changedGroupItems.put(groupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
-                                null,
-                                ConfigChangeWatcher.EventType.DELETE
-                            ));
 
-                        } else {
-                            // Don't need to notify, stay in null.
-                        }
-                    } else { //add and modify
-                        if 
(!newItemValue.equals(currentGroupItems.get(groupItemName))) {
-                            changedGroupItems.put(groupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
-                                newItemValue,
-                                ConfigChangeWatcher.EventType.MODIFY
-                            ));
-
-                        } else {
-                            // Don't need to notify, stay in the same config 
value.
-                        }
-                    }
-                });
-
-                currentGroupItems.forEach((oldGroupItemName, 
oldGroupItemValue) -> {
-                    //delete item
-                    if (null == groupItems.get(oldGroupItemName)) {
-                        // Notify watcher, the item is deleted with delete 
event type.
-                        changedGroupItems.put(oldGroupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
-                            null,
-                            ConfigChangeWatcher.EventType.DELETE
-                        ));
-                    }
-                });
-
-                if (changedGroupItems.size() > 0) {
-                    watcher.notifyGroup(changedGroupItems);
-                }
-            });
-            if (log.isTraceEnabled()) {
-                log.trace(
-                    "Current configurations after the sync." + LINE_SEPARATOR 
+ groupConfigChangeWatcherRegister.toString());
+        currentGroupItems.forEach((oldGroupItemName, oldGroupItemValue) -> {
+            //delete item
+            if (null == groupItems.get(oldGroupItemName)) {
+                // Notify watcher, the item is deleted with delete event type.
+                changedGroupItems.put(oldGroupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
+                    null,
+                    ConfigChangeWatcher.EventType.DELETE
+                ));
             }
         });
-    }
-
-    public abstract Optional<ConfigTable> readConfig(Set<String> keys);
 
-    public abstract Optional<GroupConfigTable> readGroupConfig(Set<String> 
keys);
-
-    static class Register {
-        private Map<String, WatcherHolder> register = new HashMap<>();
-
-        private boolean containsKey(String key) {
-            return register.containsKey(key);
-        }
-
-        private void put(String key, WatcherHolder holder) {
-            register.put(key, holder);
-        }
-
-        public WatcherHolder get(String name) {
-            return register.get(name);
-        }
-
-        public Set<String> keys() {
-            return register.keySet();
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder registerTableDescription = new StringBuilder();
-            registerTableDescription.append("Following dynamic config items 
are available.").append(LINE_SEPARATOR);
-            
registerTableDescription.append("---------------------------------------------").append(LINE_SEPARATOR);
-            register.forEach((key, holder) -> {
-                ConfigChangeWatcher watcher = holder.getWatcher();
-                registerTableDescription.append("key:")
-                                        .append(key)
-                                        .append("    module:")
-                                        .append(watcher.getModule())
-                                        .append("    provider:")
-                                        .append(watcher.getProvider().name());
-                if 
(watcher.watchType.equals(ConfigChangeWatcher.WatchType.GROUP)) {
-                    GroupConfigChangeWatcher groupWatcher = 
(GroupConfigChangeWatcher) watcher;
-                    registerTableDescription.append("    groupItems(current):")
-                                            .append(groupWatcher.groupItems());
-                } else {
-                    registerTableDescription.append("    value(current):")
-                                            .append(watcher.value());
-                }
-                registerTableDescription.append(LINE_SEPARATOR);
-            });
-            return registerTableDescription.toString();
+        if (changedGroupItems.size() > 0) {
+            watcher.notifyGroup(changedGroupItems);
         }
     }
 
diff --git 
a/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/FetchingConfigWatcherRegister.java
similarity index 64%
copy from 
oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegister.java
copy to 
oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/FetchingConfigWatcherRegister.java
index b4c6d23d33..9fe403927a 100644
--- 
a/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/FetchingConfigWatcherRegister.java
@@ -18,34 +18,34 @@
 
 package org.apache.skywalking.oap.server.configuration.api;
 
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import 
org.apache.skywalking.oap.server.library.util.RunnableWithExceptionProtection;
-
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import 
org.apache.skywalking.oap.server.library.util.RunnableWithExceptionProtection;
 
 /**
- * The default implementor of Config Watcher register.
+ * Implement Config Watcher register using a periodic sync task.
  */
 @Slf4j
-public abstract class ConfigWatcherRegister implements 
DynamicConfigurationService {
+public abstract class FetchingConfigWatcherRegister extends 
ConfigWatcherRegister {
     public static final String LINE_SEPARATOR = 
System.getProperty("line.separator", "\n");
+
     private final Register singleConfigChangeWatcherRegister = new Register();
     @Getter
     private final Register groupConfigChangeWatcherRegister = new Register();
     private volatile boolean isStarted = false;
     private final long syncPeriod;
 
-    public ConfigWatcherRegister() {
+    public FetchingConfigWatcherRegister() {
         this(60);
     }
 
-    public ConfigWatcherRegister(long syncPeriod) {
+    public FetchingConfigWatcherRegister(long syncPeriod) {
         this.syncPeriod = syncPeriod;
     }
 
@@ -74,10 +74,12 @@ public abstract class ConfigWatcherRegister implements 
DynamicConfigurationServi
         }
     }
 
+    @Override
     public void start() {
         isStarted = true;
 
-        log.info("Current configurations after the bootstrap sync." + 
LINE_SEPARATOR + singleConfigChangeWatcherRegister.toString());
+        log.info(
+            "Current configurations after the bootstrap sync." + 
LINE_SEPARATOR + singleConfigChangeWatcherRegister.toString());
 
         Executors.newSingleThreadScheduledExecutor()
                  .scheduleAtFixedRate(
@@ -108,25 +110,7 @@ public abstract class ConfigWatcherRegister implements 
DynamicConfigurationServi
                     return;
                 }
                 ConfigChangeWatcher watcher = holder.getWatcher();
-                String newItemValue = item.getValue();
-                if (newItemValue == null) {
-                    if (watcher.value() != null) {
-                        // Notify watcher, the new value is null with delete 
event type.
-                        watcher.notify(
-                            new ConfigChangeWatcher.ConfigChangeEvent(null, 
ConfigChangeWatcher.EventType.DELETE));
-                    } else {
-                        // Don't need to notify, stay in null.
-                    }
-                } else {
-                    if (!newItemValue.equals(watcher.value())) {
-                        watcher.notify(new 
ConfigChangeWatcher.ConfigChangeEvent(
-                            newItemValue,
-                            ConfigChangeWatcher.EventType.MODIFY
-                        ));
-                    } else {
-                        // Don't need to notify, stay in the same config value.
-                    }
-                }
+                notifySingleValue(watcher, item);
             });
             if (log.isTraceEnabled()) {
                 log.trace(
@@ -152,51 +136,7 @@ public abstract class ConfigWatcherRegister implements 
DynamicConfigurationServi
                 }
 
                 GroupConfigChangeWatcher watcher = (GroupConfigChangeWatcher) 
holder.getWatcher();
-                Map<String, ConfigTable.ConfigItem> groupItems = 
groupConfigItems.getItems();
-                Map<String, ConfigChangeWatcher.ConfigChangeEvent> 
changedGroupItems = new HashMap<>();
-                Map<String, String> currentGroupItems = 
Optional.ofNullable(watcher.groupItems())
-                                                                .orElse(new 
HashMap<>());
-
-                groupItems.forEach((groupItemName, groupItem) -> {
-                    String newItemValue = groupItem.getValue();
-                    if (newItemValue == null) {
-                        if (currentGroupItems.get(groupItemName) != null) {
-                            // Notify watcher, the new value is null with 
delete event type.
-                            changedGroupItems.put(groupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
-                                null,
-                                ConfigChangeWatcher.EventType.DELETE
-                            ));
-
-                        } else {
-                            // Don't need to notify, stay in null.
-                        }
-                    } else { //add and modify
-                        if 
(!newItemValue.equals(currentGroupItems.get(groupItemName))) {
-                            changedGroupItems.put(groupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
-                                newItemValue,
-                                ConfigChangeWatcher.EventType.MODIFY
-                            ));
-
-                        } else {
-                            // Don't need to notify, stay in the same config 
value.
-                        }
-                    }
-                });
-
-                currentGroupItems.forEach((oldGroupItemName, 
oldGroupItemValue) -> {
-                    //delete item
-                    if (null == groupItems.get(oldGroupItemName)) {
-                        // Notify watcher, the item is deleted with delete 
event type.
-                        changedGroupItems.put(oldGroupItemName, new 
ConfigChangeWatcher.ConfigChangeEvent(
-                            null,
-                            ConfigChangeWatcher.EventType.DELETE
-                        ));
-                    }
-                });
-
-                if (changedGroupItems.size() > 0) {
-                    watcher.notifyGroup(changedGroupItems);
-                }
+                notifyGroupValues(watcher, groupConfigItems);
             });
             if (log.isTraceEnabled()) {
                 log.trace(
@@ -254,18 +194,4 @@ public abstract class ConfigWatcherRegister implements 
DynamicConfigurationServi
             return registerTableDescription.toString();
         }
     }
-
-    @Getter
-    protected static class WatcherHolder {
-        private ConfigChangeWatcher watcher;
-        private final String key;
-
-        public WatcherHolder(ConfigChangeWatcher watcher) {
-            this.watcher = watcher;
-            this.key = String.join(
-                ".", watcher.getModule(), watcher.getProvider().name(),
-                watcher.getItemName()
-            );
-        }
-    }
 }
diff --git 
a/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ListeningConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ListeningConfigWatcherRegister.java
new file mode 100644
index 0000000000..c87da3d62a
--- /dev/null
+++ 
b/oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ListeningConfigWatcherRegister.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.configuration.api;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Implement Config Watcher register using client listening.
+ */
+@Slf4j
+public abstract class ListeningConfigWatcherRegister extends 
ConfigWatcherRegister {
+
+    @Override
+    synchronized public void registerConfigChangeWatcher(ConfigChangeWatcher 
watcher) {
+        startListening(new WatcherHolder(watcher), new ConfigChangeCallback() {
+            @Override
+            public synchronized void onSingleValueChanged(final WatcherHolder 
holder, final ConfigTable.ConfigItem configItem) {
+                notifySingleValue(holder.getWatcher(), configItem);
+            }
+
+            @Override
+            public synchronized void onGroupValuesChanged(final WatcherHolder 
holder,
+                                             final 
GroupConfigTable.GroupConfigItems groupConfigItems) {
+                notifyGroupValues((GroupConfigChangeWatcher) 
holder.getWatcher(), groupConfigItems);
+            }
+        });
+    }
+
+    @Override
+    public void start() {
+        // do nothing
+    }
+
+    /**
+     * listen key value defined by watcherHolder, callback should be executed 
if key value changed
+     */
+    protected abstract void startListening(WatcherHolder watcherHolder, 
ConfigChangeCallback configChangeCallback);
+
+    protected interface ConfigChangeCallback {
+        void onSingleValueChanged(WatcherHolder holder, ConfigTable.ConfigItem 
configItem);
+
+        void onGroupValuesChanged(WatcherHolder holder, 
GroupConfigTable.GroupConfigItems groupConfigItems);
+    }
+}
diff --git 
a/oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegisterTest.java
 
b/oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/FetchingConfigWatcherRegisterTest.java
similarity index 85%
rename from 
oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegisterTest.java
rename to 
oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/FetchingConfigWatcherRegisterTest.java
index 8ba7427824..82339e51a1 100644
--- 
a/oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/ConfigWatcherRegisterTest.java
+++ 
b/oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/FetchingConfigWatcherRegisterTest.java
@@ -34,8 +34,8 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-public class ConfigWatcherRegisterTest {
-    private ConfigWatcherRegister register;
+public class FetchingConfigWatcherRegisterTest {
+    private FetchingConfigWatcherRegister register;
 
     @BeforeEach
     public void setup() {
@@ -118,17 +118,17 @@ public class ConfigWatcherRegisterTest {
         });
 
         register.configSync();
-        ConfigWatcherRegister.Register registerTable = 
Whitebox.getInternalState(this.register, "singleConfigChangeWatcherRegister");
-        ConfigWatcherRegister.Register groupRegisterTable = 
Whitebox.getInternalState(this.register, "groupConfigChangeWatcherRegister");
+        FetchingConfigWatcherRegister.Register registerTable = 
Whitebox.getInternalState(this.register, "singleConfigChangeWatcherRegister");
+        FetchingConfigWatcherRegister.Register groupRegisterTable = 
Whitebox.getInternalState(this.register, "groupConfigChangeWatcherRegister");
 
-        String expected = "Following dynamic config items are available." + 
ConfigWatcherRegister.LINE_SEPARATOR + 
"---------------------------------------------" + 
ConfigWatcherRegister.LINE_SEPARATOR + "key:MockModule.provider.prop2    
module:MockModule    provider:provider    value(current):null" + 
ConfigWatcherRegister.LINE_SEPARATOR;
-        String groupConfigExpected = "Following dynamic config items are 
available." + ConfigWatcherRegister.LINE_SEPARATOR + 
"---------------------------------------------" + 
ConfigWatcherRegister.LINE_SEPARATOR + "key:MockModule.provider.groupItems1    
module:MockModule    provider:provider    groupItems(current):null" + 
ConfigWatcherRegister.LINE_SEPARATOR;
+        String expected = "Following dynamic config items are available." + 
FetchingConfigWatcherRegister.LINE_SEPARATOR + 
"---------------------------------------------" + 
FetchingConfigWatcherRegister.LINE_SEPARATOR + "key:MockModule.provider.prop2   
 module:MockModule    provider:provider    value(current):null" + 
FetchingConfigWatcherRegister.LINE_SEPARATOR;
+        String groupConfigExpected = "Following dynamic config items are 
available." + FetchingConfigWatcherRegister.LINE_SEPARATOR + 
"---------------------------------------------" + 
FetchingConfigWatcherRegister.LINE_SEPARATOR + 
"key:MockModule.provider.groupItems1    module:MockModule    provider:provider  
  groupItems(current):null" + FetchingConfigWatcherRegister.LINE_SEPARATOR;
 
         assertEquals(expected, registerTable.toString());
         assertEquals(groupConfigExpected, groupRegisterTable.toString());
     }
 
-    public static class MockConfigWatcherRegister extends 
ConfigWatcherRegister {
+    public static class MockConfigWatcherRegister extends 
FetchingConfigWatcherRegister {
 
         @Override
         public Optional<ConfigTable> readConfig(Set<String> keys) {
diff --git 
a/oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/ListeningConfigWatcherRegisterTest.java
 
b/oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/ListeningConfigWatcherRegisterTest.java
new file mode 100644
index 0000000000..8393a57092
--- /dev/null
+++ 
b/oap-server/server-configuration/configuration-api/src/test/java/org/apache/skywalking/oap/server/configuration/api/ListeningConfigWatcherRegisterTest.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.configuration.api;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import 
org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ListeningConfigWatcherRegisterTest {
+    private ListeningConfigWatcherRegister register;
+
+    @BeforeEach
+    public void setup() {
+        register = new 
ListeningConfigWatcherRegisterTest.MockConfigWatcherRegister();
+    }
+
+    @AfterEach
+    public void tearDown() {
+        register = null;
+    }
+
+    @Test
+    public void testInit() {
+        final String[] newValue = new String[1];
+
+        register.registerConfigChangeWatcher(
+            new ConfigChangeWatcher("MockModule", new 
FetchingConfigWatcherRegisterTest.MockProvider(), "prop2") {
+                @Override
+                public void notify(ConfigChangeEvent value) {
+                    newValue[0] = value.getNewValue();
+                }
+
+                @Override
+                public String value() {
+                    return null;
+                }
+            });
+
+        assertEquals("abc2", newValue[0]);
+    }
+
+    @Test
+    public void testGroupConfInit() {
+        final Map<String, String> config = new ConcurrentHashMap<>();
+
+        register.registerConfigChangeWatcher(
+            new GroupConfigChangeWatcher("MockModule", new 
FetchingConfigWatcherRegisterTest.MockProvider(),
+                                         "groupItems1"
+            ) {
+                @Override
+                public void notifyGroup(Map<String, ConfigChangeEvent> 
groupItems) {
+                    groupItems.forEach((groupItemName, event) -> {
+                        config.put(groupItemName, event.getNewValue());
+                    });
+                }
+
+                @Override
+                public Map<String, String> groupItems() {
+                    return config;
+                }
+            });
+
+        assertEquals("abc", config.get("item1"));
+        assertEquals("abc2", config.get("item2"));
+    }
+
+    public static class MockConfigWatcherRegister extends 
ListeningConfigWatcherRegister {
+
+        @Override
+        protected void startListening(final WatcherHolder watcherHolder,
+                                      final ConfigChangeCallback 
configChangeCallback) {
+            switch (watcherHolder.getKey()) {
+                case "MockModule.provider.prop1":
+                    configChangeCallback.onSingleValueChanged(
+                        watcherHolder,
+                        new 
ConfigTable.ConfigItem("MockModule.provider.prop1", "abc")
+                    );
+                    break;
+                case "MockModule.provider.prop2":
+                    configChangeCallback.onSingleValueChanged(
+                        watcherHolder,
+                        new 
ConfigTable.ConfigItem("MockModule.provider.prop1", "abc2")
+                    );
+                    break;
+                case "MockModule.provider.groupItems1":
+                    ConfigTable.ConfigItem item1 = new 
ConfigTable.ConfigItem("item1", "abc");
+                    ConfigTable.ConfigItem item2 = new 
ConfigTable.ConfigItem("item2", "abc2");
+                    GroupConfigTable.GroupConfigItems groupConfigItems1 = new 
GroupConfigTable.GroupConfigItems(
+                        "MockModule.provider.groupItems1");
+                    groupConfigItems1.add(item1);
+                    groupConfigItems1.add(item2);
+                    configChangeCallback.onGroupValuesChanged(watcherHolder, 
groupConfigItems1);
+                    break;
+                case "MockModule.provider.groupItems2":
+                    ConfigTable.ConfigItem item3 = new 
ConfigTable.ConfigItem("item3", "abc3");
+                    GroupConfigTable.GroupConfigItems groupConfigItems2 = new 
GroupConfigTable.GroupConfigItems(
+                        "MockModule.provider.groupItems2");
+                    groupConfigItems2.add(item3);
+                    configChangeCallback.onGroupValuesChanged(watcherHolder, 
groupConfigItems2);
+                    break;
+            }
+        }
+    }
+
+    public static class MockModule extends ModuleDefine {
+
+        public MockModule() {
+            super("MockModule");
+        }
+
+        @Override
+        public Class[] services() {
+            return new Class[0];
+        }
+    }
+
+    public static class MockProvider extends ModuleProvider {
+
+        @Override
+        public String name() {
+            return "provider";
+        }
+
+        @Override
+        public Class<? extends ModuleDefine> module() {
+            return FetchingConfigWatcherRegisterTest.MockModule.class;
+        }
+
+        @Override
+        public ConfigCreator newConfigCreator() {
+            return null;
+        }
+
+        @Override
+        public void prepare() throws ServiceNotProvidedException, 
ModuleStartException {
+
+        }
+
+        @Override
+        public void start() throws ServiceNotProvidedException, 
ModuleStartException {
+
+        }
+
+        @Override
+        public void notifyAfterCompleted() throws ServiceNotProvidedException, 
ModuleStartException {
+
+        }
+
+        @Override
+        public String[] requiredModules() {
+            return new String[0];
+        }
+    }
+}
diff --git 
a/oap-server/server-configuration/configuration-apollo/src/main/java/org/apache/skywalking/oap/server/configuration/apollo/ApolloConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-apollo/src/main/java/org/apache/skywalking/oap/server/configuration/apollo/ApolloConfigWatcherRegister.java
index fb1c18c98c..384e4732d3 100644
--- 
a/oap-server/server-configuration/configuration-apollo/src/main/java/org/apache/skywalking/oap/server/configuration/apollo/ApolloConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-apollo/src/main/java/org/apache/skywalking/oap/server/configuration/apollo/ApolloConfigWatcherRegister.java
@@ -21,22 +21,22 @@ package 
org.apache.skywalking.oap.server.configuration.apollo;
 import com.ctrip.framework.apollo.Config;
 import com.ctrip.framework.apollo.ConfigService;
 import com.google.common.base.Strings;
-import java.util.Optional;
+import java.util.Collections;
+import java.util.Objects;
 import java.util.Set;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
+import 
org.apache.skywalking.oap.server.configuration.api.ListeningConfigWatcherRegister;
+import org.apache.skywalking.oap.server.library.util.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ApolloConfigWatcherRegister extends ConfigWatcherRegister {
+public class ApolloConfigWatcherRegister extends 
ListeningConfigWatcherRegister {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(ApolloConfigWatcherRegister.class);
 
     private final Config configReader;
 
     public ApolloConfigWatcherRegister(ApolloConfigurationCenterSettings 
settings) {
-        super(settings.getPeriod());
-
         final String namespace = settings.getNamespace();
 
         final boolean isDefaultNamespace = Strings.isNullOrEmpty(namespace);
@@ -55,34 +55,61 @@ public class ApolloConfigWatcherRegister extends 
ConfigWatcherRegister {
     }
 
     @Override
-    public Optional<ConfigTable> readConfig(Set<String> keys) {
-        final ConfigTable configTable = new ConfigTable();
+    protected void startListening(final WatcherHolder holder, 
ConfigChangeCallback configChangeCallback) {
+        String key = holder.getKey();
+        switch (holder.getWatcher().getWatchType()) {
+            case SINGLE:
+                // read initial value before listening
+                String value = this.configReader.getProperty(key, null);
+                if (value != null) {
+                    configChangeCallback.onSingleValueChanged(holder, new 
ConfigTable.ConfigItem(key, value));
+                }
 
-        for (final String name : keys) {
-            final String value = configReader.getProperty(name, null);
-            configTable.add(new ConfigTable.ConfigItem(name, value));
-        }
+                // add change listener
+                this.configReader.addChangeListener(changeEvent -> {
+                    changeEvent.changedKeys().stream()
+                               .filter(changedKey -> 
Objects.equals(changedKey, key))
+                               .findFirst()
+                               .ifPresent(changedKey -> {
+                                   String newValue = 
changeEvent.getChange(changedKey).getNewValue();
+                                   configChangeCallback.onSingleValueChanged(
+                                       holder, new 
ConfigTable.ConfigItem(changedKey, newValue)
+                                   );
+                               });
+                }, Collections.singleton(key));
+                break;
+            case GROUP:
+                String groupPrefix = key + ".";
 
-        return Optional.of(configTable);
-    }
+                // read initial group value before listening
+                Set<String> allKeys = this.configReader.getPropertyNames();
+                if (CollectionUtils.isNotEmpty(allKeys)) {
+                    GroupConfigTable.GroupConfigItems groupConfigItems = new 
GroupConfigTable.GroupConfigItems(key);
 
-    @Override
-    public Optional<GroupConfigTable> readGroupConfig(final Set<String> keys) {
-        GroupConfigTable groupConfigTable = new GroupConfigTable();
-        Set<String> allKeys = this.configReader.getPropertyNames();
+                    allKeys.stream().filter(it -> 
it.startsWith(groupPrefix)).forEach(groupItemKey -> {
+                        String itemName = 
groupItemKey.substring(groupPrefix.length());
+                        String itemValue = 
this.configReader.getProperty(groupItemKey, null);
+                        groupConfigItems.add(new 
ConfigTable.ConfigItem(itemName, itemValue));
+                    });
 
-        keys.forEach(key -> {
-            GroupConfigTable.GroupConfigItems groupConfigItems = new 
GroupConfigTable.GroupConfigItems(key);
-            groupConfigTable.addGroupConfigItems(groupConfigItems);
-            String groupKey = key + ".";
-            if (allKeys != null) {
-                allKeys.stream().filter(it -> 
it.startsWith(groupKey)).forEach(groupItemKey -> {
-                    String itemValue = 
this.configReader.getProperty(groupItemKey, null);
-                    String itemName = 
groupItemKey.substring(groupKey.length());
-                    groupConfigItems.add(new ConfigTable.ConfigItem(itemName, 
itemValue));
-                });
-            }
-        });
-        return Optional.of(groupConfigTable);
+                    configChangeCallback.onGroupValuesChanged(holder, 
groupConfigItems);
+                }
+
+                // add change listener
+                this.configReader.addChangeListener(changeEvent -> {
+                    GroupConfigTable.GroupConfigItems newGroupConfigItems = 
new GroupConfigTable.GroupConfigItems(key);
+
+                    for (final String groupItemKey : 
changeEvent.changedKeys()) {
+                        String itemName = 
groupItemKey.substring(groupPrefix.length());
+                        String itemValue = 
changeEvent.getChange(groupItemKey).getNewValue();
+                        newGroupConfigItems.add(new 
ConfigTable.ConfigItem(itemName, itemValue));
+                    }
+
+                    configChangeCallback.onGroupValuesChanged(holder, 
newGroupConfigItems);
+                }, Collections.emptySet(), Collections.singleton(key));
+                break;
+            default:
+                throw new IllegalArgumentException("unsupported watcher 
type.");
+        }
     }
 }
diff --git 
a/oap-server/server-configuration/configuration-consul/src/main/java/org/apache/skywalking/oap/server/configuration/consul/ConsulConfigurationWatcherRegister.java
 
b/oap-server/server-configuration/configuration-consul/src/main/java/org/apache/skywalking/oap/server/configuration/consul/ConsulConfigurationWatcherRegister.java
index 2ad8b08471..349b06a449 100644
--- 
a/oap-server/server-configuration/configuration-consul/src/main/java/org/apache/skywalking/oap/server/configuration/consul/ConsulConfigurationWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-consul/src/main/java/org/apache/skywalking/oap/server/configuration/consul/ConsulConfigurationWatcherRegister.java
@@ -34,12 +34,12 @@ import java.util.stream.Collectors;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
 
 @SuppressWarnings("UnstableApiUsage")
 @Slf4j
-public class ConsulConfigurationWatcherRegister extends ConfigWatcherRegister {
+public class ConsulConfigurationWatcherRegister extends 
FetchingConfigWatcherRegister {
     private static final int DEFAULT_PORT = 8500;
 
     private final KeyValueClient consul;
diff --git 
a/oap-server/server-configuration/configuration-etcd/src/main/java/org/apache/skywalking/oap/server/configuration/etcd/EtcdConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-etcd/src/main/java/org/apache/skywalking/oap/server/configuration/etcd/EtcdConfigWatcherRegister.java
index 1478073f74..cca35e2fcf 100644
--- 
a/oap-server/server-configuration/configuration-etcd/src/main/java/org/apache/skywalking/oap/server/configuration/etcd/EtcdConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-etcd/src/main/java/org/apache/skywalking/oap/server/configuration/etcd/EtcdConfigWatcherRegister.java
@@ -29,13 +29,13 @@ import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.skywalking.oap.server.library.util.StringUtil;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
+import org.apache.skywalking.oap.server.library.util.StringUtil;
 
 @Slf4j
-public class EtcdConfigWatcherRegister extends ConfigWatcherRegister {
+public class EtcdConfigWatcherRegister extends FetchingConfigWatcherRegister {
 
     private final KV client;
 
diff --git 
a/oap-server/server-configuration/configuration-k8s-configmap/src/main/java/org/apache/skywalking/oap/server/configuration/configmap/ConfigmapConfigurationWatcherRegister.java
 
b/oap-server/server-configuration/configuration-k8s-configmap/src/main/java/org/apache/skywalking/oap/server/configuration/configmap/ConfigmapConfigurationWatcherRegister.java
index f7decac49e..e9c93707cd 100644
--- 
a/oap-server/server-configuration/configuration-k8s-configmap/src/main/java/org/apache/skywalking/oap/server/configuration/configmap/ConfigmapConfigurationWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-k8s-configmap/src/main/java/org/apache/skywalking/oap/server/configuration/configmap/ConfigmapConfigurationWatcherRegister.java
@@ -23,11 +23,11 @@ import java.util.Optional;
 import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
 
 @Slf4j
-public class ConfigmapConfigurationWatcherRegister extends 
ConfigWatcherRegister {
+public class ConfigmapConfigurationWatcherRegister extends 
FetchingConfigWatcherRegister {
 
     private final ConfigurationConfigmapInformer informer;
 
diff --git 
a/oap-server/server-configuration/configuration-nacos/src/main/java/org/apache/skywalking/oap/server/configuration/nacos/NacosConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-nacos/src/main/java/org/apache/skywalking/oap/server/configuration/nacos/NacosConfigWatcherRegister.java
index 3cd7f2771f..663af8763f 100644
--- 
a/oap-server/server-configuration/configuration-nacos/src/main/java/org/apache/skywalking/oap/server/configuration/nacos/NacosConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-nacos/src/main/java/org/apache/skywalking/oap/server/configuration/nacos/NacosConfigWatcherRegister.java
@@ -31,15 +31,14 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
-
 import lombok.extern.slf4j.Slf4j;
-import org.apache.skywalking.oap.server.library.util.StringUtil;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
+import org.apache.skywalking.oap.server.library.util.StringUtil;
 
 @Slf4j
-public class NacosConfigWatcherRegister extends ConfigWatcherRegister {
+public class NacosConfigWatcherRegister extends FetchingConfigWatcherRegister {
     private final NacosServerSettings settings;
     private final ConfigService configService;
     private final Map<String, Optional<String>> configItemKeyedByName;
diff --git 
a/oap-server/server-configuration/configuration-zookeeper/src/main/java/org/apache/skywalking/oap/server/configuration/zookeeper/ZookeeperConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-zookeeper/src/main/java/org/apache/skywalking/oap/server/configuration/zookeeper/ZookeeperConfigWatcherRegister.java
index 82eb49798c..c511eebf02 100644
--- 
a/oap-server/server-configuration/configuration-zookeeper/src/main/java/org/apache/skywalking/oap/server/configuration/zookeeper/ZookeeperConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-zookeeper/src/main/java/org/apache/skywalking/oap/server/configuration/zookeeper/ZookeeperConfigWatcherRegister.java
@@ -29,11 +29,11 @@ import org.apache.curator.framework.recipes.cache.ChildData;
 import org.apache.curator.framework.recipes.cache.PathChildrenCache;
 import org.apache.curator.retry.ExponentialBackoffRetry;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
 
 @Slf4j
-public class ZookeeperConfigWatcherRegister extends ConfigWatcherRegister {
+public class ZookeeperConfigWatcherRegister extends 
FetchingConfigWatcherRegister {
     private final CuratorFramework client;
     private final PathChildrenCache childrenCache;
     private final String prefix;
diff --git 
a/oap-server/server-configuration/configuration-zookeeper/src/test/java/org/apache/skywalking/oap/server/configuration/zookeeper/ut/MockZookeeperConfigWatcherRegister.java
 
b/oap-server/server-configuration/configuration-zookeeper/src/test/java/org/apache/skywalking/oap/server/configuration/zookeeper/ut/MockZookeeperConfigWatcherRegister.java
index 3b2800e689..bc08f75b9f 100644
--- 
a/oap-server/server-configuration/configuration-zookeeper/src/test/java/org/apache/skywalking/oap/server/configuration/zookeeper/ut/MockZookeeperConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/configuration-zookeeper/src/test/java/org/apache/skywalking/oap/server/configuration/zookeeper/ut/MockZookeeperConfigWatcherRegister.java
@@ -23,11 +23,11 @@ import java.util.Set;
 import org.apache.curator.framework.recipes.cache.ChildData;
 import org.apache.curator.framework.recipes.cache.PathChildrenCache;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
 import 
org.apache.skywalking.oap.server.configuration.zookeeper.ZookeeperServerSettings;
 
-public class MockZookeeperConfigWatcherRegister extends ConfigWatcherRegister {
+public class MockZookeeperConfigWatcherRegister extends 
FetchingConfigWatcherRegister {
     private PathChildrenCache childrenCache;
     private final String prefix;
 
diff --git 
a/oap-server/server-configuration/grpc-configuration-sync/src/main/java/org/apache/skywalking/oap/server/configuration/grpc/GRPCConfigWatcherRegister.java
 
b/oap-server/server-configuration/grpc-configuration-sync/src/main/java/org/apache/skywalking/oap/server/configuration/grpc/GRPCConfigWatcherRegister.java
index b10594877a..7fd028261b 100644
--- 
a/oap-server/server-configuration/grpc-configuration-sync/src/main/java/org/apache/skywalking/oap/server/configuration/grpc/GRPCConfigWatcherRegister.java
+++ 
b/oap-server/server-configuration/grpc-configuration-sync/src/main/java/org/apache/skywalking/oap/server/configuration/grpc/GRPCConfigWatcherRegister.java
@@ -24,7 +24,7 @@ import java.util.Optional;
 import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
-import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
 import 
org.apache.skywalking.oap.server.configuration.service.ConfigurationRequest;
 import 
org.apache.skywalking.oap.server.configuration.service.ConfigurationResponse;
@@ -32,7 +32,7 @@ import 
org.apache.skywalking.oap.server.configuration.service.ConfigurationServi
 import 
org.apache.skywalking.oap.server.configuration.service.GroupConfigurationResponse;
 
 @Slf4j
-public class GRPCConfigWatcherRegister extends ConfigWatcherRegister {
+public class GRPCConfigWatcherRegister extends FetchingConfigWatcherRegister {
     private RemoteEndpointSettings settings;
     private ConfigurationServiceGrpc.ConfigurationServiceBlockingStub stub;
     private String uuid = null;
diff --git 
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/ApdexThresholdConfigTest.java
 
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/ApdexThresholdConfigTest.java
index 789efa8405..43885dc15d 100644
--- 
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/ApdexThresholdConfigTest.java
+++ 
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/ApdexThresholdConfigTest.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.oap.server.core.analysis;
 
 import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
 import 
org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
+import 
org.apache.skywalking.oap.server.configuration.api.FetchingConfigWatcherRegister;
 import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
 import org.apache.skywalking.oap.server.core.CoreModuleProvider;
 import org.junit.jupiter.api.Test;
@@ -65,7 +66,7 @@ public class ApdexThresholdConfigTest {
         assertThat(config.lookup("bar")).isEqualTo(1000);
     }
 
-    public static class MockConfigWatcherRegister extends 
ConfigWatcherRegister {
+    public static class MockConfigWatcherRegister extends 
FetchingConfigWatcherRegister {
 
         public MockConfigWatcherRegister(long syncPeriod) {
             super(syncPeriod);
diff --git a/oap-server/server-starter/src/main/resources/application.yml 
b/oap-server/server-starter/src/main/resources/application.yml
index 7bd69689a1..fd62d0355e 100644
--- a/oap-server/server-starter/src/main/resources/application.yml
+++ b/oap-server/server-starter/src/main/resources/application.yml
@@ -471,7 +471,6 @@ configuration:
     apolloCluster: ${SW_CONFIG_APOLLO_CLUSTER:default}
     apolloEnv: ${SW_CONFIG_APOLLO_ENV:""}
     appId: ${SW_CONFIG_APOLLO_APP_ID:skywalking}
-    period: ${SW_CONFIG_APOLLO_PERIOD:60}
   zookeeper:
     period: ${SW_CONFIG_ZK_PERIOD:60} # Unit seconds, sync period. Default 
fetch every 60 seconds.
     namespace: ${SW_CONFIG_ZK_NAMESPACE:/default}

Reply via email to