wujimin closed pull request #809: [SCB-729] Instance cache check
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/809
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java
 
b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java
index d913dc999..ca91ef617 100644
--- 
a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java
+++ 
b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java
@@ -18,6 +18,8 @@
 package org.apache.servicecomb.foundation.test.scaffolding.config;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.springframework.util.ReflectionUtils;
 
@@ -40,23 +42,34 @@
   private static final Field FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL =
       ReflectionUtils.findField(DynamicProperty.class, 
"dynamicPropertySupportImpl");
 
+  private static final Field FIELD_DYNAMIC_PROPERTY_ALL_PROPS = ReflectionUtils
+      .findField(DynamicProperty.class, "ALL_PROPS");
+
+  private static Method updatePropertyMethod =
+      ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", 
String.class, Object.class);
+
   static {
     FIELD_INSTANCE.setAccessible(true);
     FIELD_CUSTOM_CONFIGURATION_INSTALLED.setAccessible(true);
     FIELD_CONFIG.setAccessible(true);
     FIELD_INITIALIZED_WITH_DEFAULT_CONFIG.setAccessible(true);
     FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL.setAccessible(true);
+    FIELD_DYNAMIC_PROPERTY_ALL_PROPS.setAccessible(true);
+    updatePropertyMethod.setAccessible(true);
   }
 
   private ArchaiusUtils() {
   }
 
+  @SuppressWarnings("unchecked")
   public static void resetConfig() {
     ReflectionUtils.setField(FIELD_INSTANCE, null, null);
     ReflectionUtils.setField(FIELD_CUSTOM_CONFIGURATION_INSTALLED, null, 
false);
     ReflectionUtils.setField(FIELD_CONFIG, null, null);
     ReflectionUtils.setField(FIELD_INITIALIZED_WITH_DEFAULT_CONFIG, null, 
false);
     ReflectionUtils.setField(FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL, null, null);
+    ((ConcurrentHashMap<String, DynamicProperty>) 
ReflectionUtils.getField(FIELD_DYNAMIC_PROPERTY_ALL_PROPS, null))
+        .clear();
   }
 
   public static void setProperty(String key, Object value) {
@@ -67,4 +80,13 @@ public static void setProperty(String key, Object value) {
         .getBackingConfigurationSource();
     config.getConfiguration(0).addProperty(key, value);
   }
+
+  /**
+   * difference with setProperty is that, updateProperty value can be null
+   * @param key
+   * @param value
+   */
+  public static void updateProperty(String key, Object value) {
+    ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value);
+  }
 }
diff --git 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java
 
b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java
index 5f7f2821f..44b86b267 100644
--- 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java
+++ 
b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java
@@ -422,12 +422,12 @@ public static OperationMeta getMockOperationMeta(String 
microserviceName, String
   }
 
   public static void setConfig(String key, int value) {
-    Utils.updateProperty(key, value);
+    ArchaiusUtils.setProperty(key, value);
   }
 
   public static void setConfigWithDefaultPrefix(String key, int value) {
     String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key;
-    Utils.updateProperty(configKey, value);
+    ArchaiusUtils.setProperty(configKey, value);
   }
 
   public static void clearState(QpsControllerManager qpsControllerManager) {
diff --git 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java
 
b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java
index 540d3031c..15d1e29b1 100644
--- 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java
+++ 
b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java
@@ -17,7 +17,10 @@
 
 package org.apache.servicecomb.qps;
 
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import org.junit.AfterClass;
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -25,20 +28,30 @@
  *
  */
 public class TestConfig {
+  @BeforeClass
+  public static void classSetup() {
+    ArchaiusUtils.resetConfig();
+  }
+
+  @AfterClass
+  public static void classTeardown() {
+    ArchaiusUtils.resetConfig();
+  }
+
   @Test
   public void testEnabled() {
     Assert.assertEquals(true, Config.INSTANCE.isProviderEnabled());
-    Utils.updateProperty(Config.PROVIDER_ENABLED, false);
+    ArchaiusUtils.updateProperty(Config.PROVIDER_ENABLED, false);
     Assert.assertEquals(false, Config.INSTANCE.isProviderEnabled());
 
-    Utils.updateProperty(Config.PROVIDER_ENABLED, null);
+    ArchaiusUtils.updateProperty(Config.PROVIDER_ENABLED, null);
     Assert.assertEquals(true, Config.INSTANCE.isProviderEnabled());
 
     Assert.assertEquals(true, Config.INSTANCE.isConsumerEnabled());
-    Utils.updateProperty(Config.CONSUMER_ENABLED, false);
+    ArchaiusUtils.updateProperty(Config.CONSUMER_ENABLED, false);
     Assert.assertEquals(false, Config.INSTANCE.isConsumerEnabled());
 
-    Utils.updateProperty(Config.CONSUMER_ENABLED, null);
+    ArchaiusUtils.updateProperty(Config.CONSUMER_ENABLED, null);
     Assert.assertEquals(true, Config.INSTANCE.isConsumerEnabled());
   }
 }
diff --git 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java
 
b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java
index d14e6a25a..f671eeab0 100644
--- 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java
+++ 
b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java
@@ -56,7 +56,7 @@
   public void setUP() {
     ArchaiusUtils.resetConfig();
     
QpsControllerManagerTest.clearState(ProviderQpsFlowControlHandler.qpsControllerMgr);
-    Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + "test", 1);
+    ArchaiusUtils.setProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + "test", 1);
   }
 
 
@@ -86,7 +86,7 @@ public void testGlobalQpsControl(final @Injectable Invocation 
invocation,
     ProviderQpsFlowControlHandler gHandler = new 
ProviderQpsFlowControlHandler();
     gHandler.handle(invocation, asyncResp);
 
-    Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, 3);
+    ArchaiusUtils.setProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, 3);
 
     expectedException.expect(RuntimeException.class);
     expectedException.expectMessage("test error");
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
index c753feab5..69e5acd40 100644
--- 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
@@ -171,8 +171,11 @@ public String getMicroserviceId(String appId, String 
microserviceName, String st
 
   @Override
   public String registerMicroservice(Microservice microservice) {
-    String serviceId =
-        microservice.getServiceId() == null ? UUID.randomUUID().toString() : 
microservice.getServiceId();
+    String serviceId = microservice.getServiceId();
+    if (serviceId == null) {
+      serviceId = UUID.randomUUID().toString();
+      microservice.setServiceId(serviceId);
+    }
     microserviceIdMap.put(serviceId, microservice);
 
     microserviceInstanceMap.computeIfAbsent(serviceId, k -> new 
ConcurrentHashMap<>());
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java
index 8093ceb5e..7cc45e221 100644
--- 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java
@@ -43,6 +43,10 @@ public MicroserviceVersionFactory 
getMicroserviceVersionFactory() {
     return microserviceVersionFactory;
   }
 
+  public Map<String, MicroserviceManager> getApps() {
+    return apps;
+  }
+
   public void setMicroserviceVersionFactory(MicroserviceVersionFactory 
microserviceVersionFactory) {
     this.microserviceVersionFactory = microserviceVersionFactory;
   }
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java
index a7bd4ae3d..2d16d0d95 100644
--- 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java
@@ -45,6 +45,10 @@ public MicroserviceManager(AppManager appManager, String 
appId) {
     appManager.getEventBus().register(this);
   }
 
+  public Map<String, MicroserviceVersions> getVersionsByName() {
+    return versionsByName;
+  }
+
   public MicroserviceVersions getOrCreateMicroserviceVersions(String 
microserviceName) {
     MicroserviceVersions microserviceVersions = 
versionsByName.computeIfAbsent(microserviceName, name -> {
       MicroserviceVersions instance = new MicroserviceVersions(appManager, 
appId, microserviceName);
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java
index 169328eca..f867f6ae7 100644
--- 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java
@@ -17,6 +17,7 @@
 
 package org.apache.servicecomb.serviceregistry.consumer;
 
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -48,8 +49,15 @@
 
   private String microserviceName;
 
+  // revision and pulledInstances directly equals to SC's response
   private String revision = null;
 
+  private List<MicroserviceInstance> pulledInstances;
+
+  // instances not always equals to pulledInstances
+  // in the future:
+  //  pulledInstances means all instance
+  //  instances means available instance
   private List<MicroserviceInstance> instances;
 
   // key is service id
@@ -101,6 +109,14 @@ public String getMicroserviceName() {
     return (T) versions.get(serviceId);
   }
 
+  public String getRevision() {
+    return revision;
+  }
+
+  public List<MicroserviceInstance> getPulledInstances() {
+    return pulledInstances;
+  }
+
   public void submitPull() {
     pendingPullCount.incrementAndGet();
 
@@ -127,7 +143,9 @@ public void pullInstances() {
     if (!microserviceInstances.isNeedRefresh()) {
       return;
     }
-    List<MicroserviceInstance> pulledInstances = 
microserviceInstances.getInstancesResponse().getInstances();
+
+    pulledInstances = 
microserviceInstances.getInstancesResponse().getInstances();
+    
pulledInstances.sort(Comparator.comparing(MicroserviceInstance::getInstanceId));
     String rev = microserviceInstances.getRevision();
 
     safeSetInstances(pulledInstances, rev);
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/Status.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/Status.java
new file mode 100644
index 000000000..47aedd4fd
--- /dev/null
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/Status.java
@@ -0,0 +1,24 @@
+/*
+ * 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.servicecomb.serviceregistry.diagnosis;
+
+public enum Status {
+  NORMAL,
+  ABNORMAL,
+  UNKNOWN
+}
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheCheckTask.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheCheckTask.java
new file mode 100644
index 000000000..b6dd6155c
--- /dev/null
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheCheckTask.java
@@ -0,0 +1,139 @@
+/*
+ * 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.servicecomb.serviceregistry.diagnosis.instance;
+
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.servicecomb.serviceregistry.consumer.AppManager;
+import org.apache.servicecomb.serviceregistry.registry.RemoteServiceRegistry;
+import 
org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.eventbus.EventBus;
+import com.netflix.config.DynamicIntProperty;
+import com.netflix.config.DynamicPropertyFactory;
+import com.netflix.config.DynamicStringProperty;
+
+import io.vertx.core.json.Json;
+
+public class InstanceCacheCheckTask implements ServiceRegistryTaskInitializer {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(InstanceCacheCheckTask.class);
+
+  private static final int DEFAULT_DIAGNOSE_INSTANCE_CACHE_INTERVAL_IN_HOUR = 
24;
+
+  private static final String CONFIG_PREFIX = 
"servicecomb.service.registry.instance.diagnose.";
+
+  public static final String MANUAL = CONFIG_PREFIX + "manual";
+
+  public static final String AUTO_INTERVAL = CONFIG_PREFIX + "interval";
+
+  // auto task
+  private ScheduledFuture<?> scheduledFuture;
+
+  private AppManager appManager;
+
+  private ScheduledThreadPoolExecutor taskPool;
+
+  private EventBus eventBus;
+
+  private DynamicIntProperty autoCheckIntervalProperty;
+
+  private DynamicStringProperty manualCheckProperty;
+
+  private TimeUnit timeUnit = TimeUnit.HOURS;
+
+  // make test easier
+  public void setTimeUnit(TimeUnit timeUnit) {
+    this.timeUnit = timeUnit;
+  }
+
+  public void setAppManager(AppManager appManager) {
+    this.appManager = appManager;
+  }
+
+  public void setTaskPool(ScheduledThreadPoolExecutor taskPool) {
+    this.taskPool = taskPool;
+  }
+
+  public void setEventBus(EventBus eventBus) {
+    this.eventBus = eventBus;
+  }
+
+  public DynamicStringProperty getManualCheckProperty() {
+    return manualCheckProperty;
+  }
+
+  public DynamicIntProperty getAutoCheckIntervalProperty() {
+    return autoCheckIntervalProperty;
+  }
+
+  @Override
+  public void init(RemoteServiceRegistry remoteServiceRegistry) {
+    appManager = remoteServiceRegistry.getAppManager();
+    taskPool = remoteServiceRegistry.getTaskPool();
+    eventBus = remoteServiceRegistry.getEventBus();
+
+    init();
+  }
+
+  protected void init() {
+    startAutoTask();
+    registerManualTask();
+  }
+
+  private void registerManualTask() {
+    // if manual config item changed, then run task once
+    manualCheckProperty = 
DynamicPropertyFactory.getInstance().getStringProperty(MANUAL, null, 
this::runTask);
+  }
+
+  protected void startAutoTask() {
+    autoCheckIntervalProperty = 
DynamicPropertyFactory.getInstance().getIntProperty(AUTO_INTERVAL,
+        DEFAULT_DIAGNOSE_INSTANCE_CACHE_INTERVAL_IN_HOUR,
+        this::doStartAutoTask);
+    doStartAutoTask();
+  }
+
+  private void doStartAutoTask() {
+    if (scheduledFuture != null) {
+      scheduledFuture.cancel(false);
+      scheduledFuture = null;
+    }
+
+    int interval = autoCheckIntervalProperty.get();
+    if (interval <= 0) {
+      LOGGER.info("disable instance cache check task, interval={}.", interval);
+      return;
+    }
+
+    scheduledFuture = taskPool.scheduleAtFixedRate(this::runTask, interval, 
interval, timeUnit);
+  }
+
+  protected void runTask() {
+    try {
+      InstanceCacheChecker checker = new InstanceCacheChecker(appManager);
+      InstanceCacheSummary instanceCacheSummary = checker.check();
+      eventBus.post(instanceCacheSummary);
+
+      LOGGER.info("check instance cache, result={}.", 
Json.encode(instanceCacheSummary));
+    } catch (Throwable e) {
+      LOGGER.error("failed check instance cache..", e);
+    }
+  }
+}
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheChecker.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheChecker.java
new file mode 100644
index 000000000..8e0e1d7a7
--- /dev/null
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheChecker.java
@@ -0,0 +1,140 @@
+/*
+ * 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.servicecomb.serviceregistry.diagnosis.instance;
+
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import 
org.apache.servicecomb.serviceregistry.api.registry.MicroserviceInstance;
+import 
org.apache.servicecomb.serviceregistry.client.http.MicroserviceInstances;
+import org.apache.servicecomb.serviceregistry.consumer.AppManager;
+import org.apache.servicecomb.serviceregistry.consumer.MicroserviceManager;
+import org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions;
+import org.apache.servicecomb.serviceregistry.definition.DefinitionConst;
+import org.apache.servicecomb.serviceregistry.diagnosis.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.core.json.Json;
+
+public class InstanceCacheChecker {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(InstanceCacheChecker.class);
+
+  private AppManager appManager;
+
+  private Set<Status> statuses = new HashSet<>();
+
+  private InstanceCacheSummary instanceCacheSummary = new 
InstanceCacheSummary();
+
+  public InstanceCacheChecker(AppManager appManager) {
+    this.appManager = appManager;
+  }
+
+  public InstanceCacheSummary check() {
+    instanceCacheSummary.setAppId(RegistryUtils.getMicroservice().getAppId());
+    
instanceCacheSummary.setMicroserviceName(RegistryUtils.getMicroservice().getServiceName());
+    instanceCacheSummary.setTimestamp(System.currentTimeMillis());
+
+    for (MicroserviceManager microserviceManager : 
appManager.getApps().values()) {
+      for (MicroserviceVersions microserviceVersions : 
microserviceManager.getVersionsByName().values()) {
+        InstanceCacheResult instanceCacheResult = check(microserviceVersions);
+        addInstanceCacheResult(instanceCacheResult);
+      }
+    }
+
+    generateStatus();
+
+    return instanceCacheSummary;
+  }
+
+  private void addInstanceCacheResult(InstanceCacheResult instanceCacheResult) 
{
+    statuses.add(instanceCacheResult.getStatus());
+    instanceCacheSummary.getProducers().add(instanceCacheResult);
+  }
+
+  protected InstanceCacheResult check(MicroserviceVersions 
microserviceVersions) {
+    InstanceCacheResult instanceCacheResult = new InstanceCacheResult();
+    instanceCacheResult.setAppId(microserviceVersions.getAppId());
+    
instanceCacheResult.setMicroserviceName(microserviceVersions.getMicroserviceName());
+
+    MicroserviceInstances microserviceInstances = RegistryUtils
+        .findServiceInstances(microserviceVersions.getAppId(),
+            microserviceVersions.getMicroserviceName(),
+            DefinitionConst.VERSION_RULE_ALL,
+            null);
+    if (microserviceInstances == null) {
+      instanceCacheResult.setStatus(Status.UNKNOWN);
+      instanceCacheResult.setDetail("failed to find instances from service 
center");
+      return instanceCacheResult;
+    }
+    if (microserviceInstances.isMicroserviceNotExist()) {
+      // no problem, will be deleted from MicroserviceManager in next pull
+      instanceCacheResult.setStatus(Status.UNKNOWN);
+      instanceCacheResult.setDetail("microservice is not exist anymore, will 
be deleted from memory in next pull");
+      return instanceCacheResult;
+    }
+
+    if (!Objects.equals(microserviceInstances.getRevision(), 
microserviceVersions.getRevision())) {
+      // maybe not pull, wait for next pull we get the same revision
+      instanceCacheResult.setStatus(Status.UNKNOWN);
+      instanceCacheResult.setDetail(String.format(
+          "revision is different, will be synchronized in next pull. local 
revision=%s, remote revision=%s",
+          microserviceVersions.getRevision(), 
microserviceInstances.getRevision()));
+      return instanceCacheResult;
+    }
+
+    // compare all instances
+    List<MicroserviceInstance> remoteInstances = 
microserviceInstances.getInstancesResponse().getInstances();
+//    if (RandomUtils.nextInt(0, 2) == 0) {
+//      MicroserviceInstance microserviceInstance = new MicroserviceInstance();
+//      microserviceInstance.setInstanceId("abc");
+//      remoteInstances.add(microserviceInstance);
+//    }
+    
remoteInstances.sort(Comparator.comparing(MicroserviceInstance::getInstanceId));
+    String local = Json.encode(microserviceVersions.getPulledInstances());
+    String remote = Json.encode(remoteInstances);
+    if (local.equals(remote)) {
+      instanceCacheResult.setStatus(Status.NORMAL);
+      return instanceCacheResult;
+    }
+
+    LOGGER.error("instance cache not match. appId={}, microservice={}.\n"
+            + "local cache: {}\n"
+            + "remote cache: {}",
+        microserviceVersions.getAppId(),
+        microserviceVersions.getMicroserviceName(),
+        local,
+        remote);
+    instanceCacheResult.setStatus(Status.ABNORMAL);
+    instanceCacheResult.setDetail("instance cache not match");
+    return instanceCacheResult;
+  }
+
+  protected void generateStatus() {
+    if (statuses.contains(Status.ABNORMAL)) {
+      instanceCacheSummary.setStatus(Status.ABNORMAL);
+    } else if (statuses.contains(Status.UNKNOWN)) {
+      instanceCacheSummary.setStatus(Status.UNKNOWN);
+    } else {
+      instanceCacheSummary.setStatus(Status.NORMAL);
+    }
+  }
+}
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheResult.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheResult.java
new file mode 100644
index 000000000..a5ae3de63
--- /dev/null
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheResult.java
@@ -0,0 +1,62 @@
+/*
+ * 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.servicecomb.serviceregistry.diagnosis.instance;
+
+import org.apache.servicecomb.serviceregistry.diagnosis.Status;
+
+public class InstanceCacheResult {
+  // producer appId and microserviceName
+  private String appId;
+
+  private String microserviceName;
+
+  private Status status;
+
+  private String detail;
+
+  public String getAppId() {
+    return appId;
+  }
+
+  public void setAppId(String appId) {
+    this.appId = appId;
+  }
+
+  public String getMicroserviceName() {
+    return microserviceName;
+  }
+
+  public void setMicroserviceName(String microserviceName) {
+    this.microserviceName = microserviceName;
+  }
+
+  public Status getStatus() {
+    return status;
+  }
+
+  public void setStatus(Status status) {
+    this.status = status;
+  }
+
+  public String getDetail() {
+    return detail;
+  }
+
+  public void setDetail(String detail) {
+    this.detail = detail;
+  }
+}
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheSummary.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheSummary.java
new file mode 100644
index 000000000..9a02cf8fc
--- /dev/null
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheSummary.java
@@ -0,0 +1,79 @@
+/*
+ * 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.servicecomb.serviceregistry.diagnosis.instance;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.servicecomb.serviceregistry.diagnosis.Status;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public class InstanceCacheSummary {
+  // consumer appId and microserviceName
+  @JsonIgnore
+  private String appId;
+
+  @JsonIgnore
+  private String microserviceName;
+
+  private Status status;
+
+  private List<InstanceCacheResult> producers = new ArrayList<>();
+
+  private long timestamp;
+
+  public String getAppId() {
+    return appId;
+  }
+
+  public void setAppId(String appId) {
+    this.appId = appId;
+  }
+
+  public String getMicroserviceName() {
+    return microserviceName;
+  }
+
+  public void setMicroserviceName(String microserviceName) {
+    this.microserviceName = microserviceName;
+  }
+
+  public Status getStatus() {
+    return status;
+  }
+
+  public void setStatus(Status status) {
+    this.status = status;
+  }
+
+  public long getTimestamp() {
+    return timestamp;
+  }
+
+  public void setTimestamp(long timestamp) {
+    this.timestamp = timestamp;
+  }
+
+  public List<InstanceCacheResult> getProducers() {
+    return producers;
+  }
+
+  public void setProducers(List<InstanceCacheResult> producers) {
+    this.producers = producers;
+  }
+}
diff --git 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java
index e6efa79a6..8eb4fb261 100644
--- 
a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java
@@ -16,9 +16,11 @@
  */
 package org.apache.servicecomb.serviceregistry.registry;
 
+import java.util.List;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 import org.apache.servicecomb.serviceregistry.client.ServiceRegistryClient;
 import 
org.apache.servicecomb.serviceregistry.client.http.ServiceRegistryClientImpl;
 import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
@@ -38,6 +40,9 @@
 
   private ScheduledThreadPoolExecutor taskPool;
 
+  private List<ServiceRegistryTaskInitializer> taskInitializers = 
SPIServiceUtils
+      .getOrLoadSortedService(ServiceRegistryTaskInitializer.class);
+
   public RemoteServiceRegistry(EventBus eventBus, ServiceRegistryConfig 
serviceRegistryConfig,
       MicroserviceDefinition microserviceDefinition) {
     super(eventBus, serviceRegistryConfig, microserviceDefinition);
@@ -77,6 +82,10 @@ public void run() {
         serviceRegistryConfig.getInstancePullInterval(),
         serviceRegistryConfig.getInstancePullInterval(),
         TimeUnit.SECONDS);
+
+    for (ServiceRegistryTaskInitializer initializer : taskInitializers) {
+      initializer.init(this);
+    }
   }
 
   @Subscribe
@@ -91,8 +100,7 @@ public void 
onMicroserviceRegistryTask(MicroserviceRegisterTask event) {
     }
   }
 
-  // for testing
-  ScheduledThreadPoolExecutor getTaskPool() {
+  public ScheduledThreadPoolExecutor getTaskPool() {
     return this.taskPool;
   }
 }
diff --git 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/Utils.java
 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/ServiceRegistryTaskInitializer.java
similarity index 60%
rename from 
handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/Utils.java
rename to 
service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/ServiceRegistryTaskInitializer.java
index e4ceb5308..21cd84258 100644
--- 
a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/Utils.java
+++ 
b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/ServiceRegistryTaskInitializer.java
@@ -15,23 +15,12 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.qps;
+package org.apache.servicecomb.serviceregistry.registry;
 
-import java.lang.reflect.Method;
-
-import org.springframework.util.ReflectionUtils;
-
-import com.netflix.config.DynamicProperty;
-
-public class Utils {
-  private static Method updatePropertyMethod =
-      ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", 
String.class, Object.class);
-
-  static {
-    updatePropertyMethod.setAccessible(true);
+public interface ServiceRegistryTaskInitializer {
+  default int getOrder() {
+    return 0;
   }
 
-  public static void updateProperty(String key, Object value) {
-    ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value);
-  }
+  void init(RemoteServiceRegistry remoteServiceRegistry);
 }
diff --git 
a/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer
 
b/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer
new file mode 100644
index 000000000..761ee02f5
--- /dev/null
+++ 
b/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+org.apache.servicecomb.serviceregistry.diagnosis.instance.InstanceCacheCheckTask
diff --git 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java
 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java
index 8bd0a2d23..9614048ca 100644
--- 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java
+++ 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java
@@ -73,6 +73,7 @@
 
   @Before
   public void setUp() throws Exception {
+    ArchaiusUtils.resetConfig();
     microserviceInstances = new MicroserviceInstances();
     findInstancesResponse = new FindInstancesResponse();
   }
diff --git 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheCheckTask.java
 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheCheckTask.java
new file mode 100644
index 000000000..862cf6fad
--- /dev/null
+++ 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheCheckTask.java
@@ -0,0 +1,151 @@
+/*
+ * 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.servicecomb.serviceregistry.diagnosis.instance;
+
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.xml.ws.Holder;
+
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import org.apache.servicecomb.serviceregistry.consumer.AppManager;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
+import io.vertx.core.json.Json;
+import mockit.Deencapsulation;
+import mockit.Mock;
+import mockit.MockUp;
+import mockit.Mocked;
+
+public class TestInstanceCacheCheckTask {
+  @Mocked
+  AppManager appManager;
+
+  ScheduledThreadPoolExecutor taskPool = new ScheduledThreadPoolExecutor(2,
+      task -> new Thread(task, "Service Center Task test thread"),
+      (task, executor) -> System.out.println("Too many pending tasks, reject " 
+ task.getClass().getName()));
+
+  EventBus eventBus = new EventBus();
+
+  InstanceCacheCheckTask task = new InstanceCacheCheckTask();
+
+  InstanceCacheSummary result;
+
+  @Before
+  public void setUp() {
+    task.setAppManager(appManager);
+    task.setTaskPool(taskPool);
+    task.setEventBus(eventBus);
+    task.setTimeUnit(TimeUnit.MILLISECONDS);
+
+    new MockUp<InstanceCacheChecker>() {
+      @Mock
+      InstanceCacheSummary check() {
+        return new InstanceCacheSummary();
+      }
+    };
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    ArchaiusUtils.resetConfig();
+    taskPool.shutdownNow();
+  }
+
+  @Test
+  public void manualTask() throws InterruptedException {
+
+    ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 0);
+    CountDownLatch latch = new CountDownLatch(1);
+    eventBus.register(new Object() {
+      @Subscribe
+      public void onChecked(InstanceCacheSummary instanceCacheSummary) {
+        result = instanceCacheSummary;
+        latch.countDown();
+      }
+    });
+    task.init();
+
+    ArchaiusUtils.setProperty(InstanceCacheCheckTask.MANUAL, 
UUID.randomUUID().toString());
+    latch.await();
+
+    Assert.assertEquals("{\"status\":null,\"producers\":[],\"timestamp\":0}", 
Json.encode(result));
+  }
+
+  @Test
+  public void autoTask_normal() throws InterruptedException {
+    ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 1);
+    CountDownLatch latch = new CountDownLatch(1);
+    eventBus.register(new Object() {
+      @Subscribe
+      public void onChecked(InstanceCacheSummary instanceCacheSummary) {
+        result = instanceCacheSummary;
+        ((ScheduledFuture<?>) Deencapsulation.getField(task, 
"scheduledFuture")).cancel(false);
+        latch.countDown();
+      }
+    });
+    task.init();
+
+    latch.await();
+    Assert.assertNotNull(Deencapsulation.getField(task, "scheduledFuture"));
+    Assert.assertEquals("{\"status\":null,\"producers\":[],\"timestamp\":0}", 
Json.encode(result));
+  }
+
+  @Test
+  public void autoTask_clearOldTask() {
+    Holder<Boolean> cancelResult = new Holder<>();
+    ScheduledFuture<?> scheduledFuture = new MockUp<ScheduledFuture>() {
+      @Mock
+      boolean cancel(boolean mayInterruptIfRunning) {
+        cancelResult.value = true;
+        return true;
+      }
+    }.getMockInstance();
+
+    ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 0);
+    Deencapsulation.setField(task, "scheduledFuture", scheduledFuture);
+    task.init();
+
+    Assert.assertNull(Deencapsulation.getField(task, "scheduledFuture"));
+    Assert.assertTrue(cancelResult.value);
+  }
+
+  @Test
+  public void autoTask_invalidIntervalZero() {
+    ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 0);
+    task.init();
+
+    Assert.assertNull(Deencapsulation.getField(task, "scheduledFuture"));
+  }
+
+  @Test
+  public void autoTask_invalidIntervalLessThanZero() {
+    ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, -1);
+    task.init();
+
+    Assert.assertNull(Deencapsulation.getField(task, "scheduledFuture"));
+  }
+}
diff --git 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheChecker.java
 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheChecker.java
new file mode 100644
index 000000000..d4d55bf23
--- /dev/null
+++ 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheChecker.java
@@ -0,0 +1,245 @@
+/*
+ * 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.servicecomb.serviceregistry.diagnosis.instance;
+
+import java.util.ArrayList;
+
+import javax.xml.ws.Holder;
+
+import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import org.apache.servicecomb.serviceregistry.ServiceRegistry;
+import org.apache.servicecomb.serviceregistry.api.registry.Microservice;
+import 
org.apache.servicecomb.serviceregistry.api.registry.MicroserviceInstance;
+import 
org.apache.servicecomb.serviceregistry.api.response.FindInstancesResponse;
+import 
org.apache.servicecomb.serviceregistry.client.http.MicroserviceInstances;
+import org.apache.servicecomb.serviceregistry.definition.DefinitionConst;
+import org.apache.servicecomb.serviceregistry.diagnosis.Status;
+import org.apache.servicecomb.serviceregistry.registry.ServiceRegistryFactory;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.vertx.core.json.Json;
+import mockit.Mock;
+import mockit.MockUp;
+
+public class TestInstanceCacheChecker {
+  ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal();
+
+  InstanceCacheChecker checker;
+
+  InstanceCacheSummary expectedSummary = new InstanceCacheSummary();
+
+  String appId = "appId";
+
+  String microserviceName = "msName";
+
+  @Before
+  public void setUp() throws Exception {
+    serviceRegistry.init();
+    RegistryUtils.setServiceRegistry(serviceRegistry);
+
+    checker = new InstanceCacheChecker(serviceRegistry.getAppManager());
+    expectedSummary.setStatus(Status.NORMAL);
+    expectedSummary.setTimestamp(1);
+
+    new MockUp<System>() {
+      @Mock
+      long currentTimeMillis() {
+        return 1L;
+      }
+    };
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    RegistryUtils.setServiceRegistry(null);
+  }
+
+  @Test
+  public void check_appManager_empty() {
+    InstanceCacheSummary instanceCacheSummary = checker.check();
+
+    Assert.assertEquals(Json.encode(expectedSummary), 
Json.encode(instanceCacheSummary));
+  }
+
+  @Test
+  public void check_microserviceManager_empty() {
+    appId = "notExist";
+    serviceRegistry.getAppManager().getOrCreateMicroserviceVersions(appId, 
microserviceName);
+    InstanceCacheSummary instanceCacheSummary = checker.check();
+
+    InstanceCacheResult instanceCacheResult = new InstanceCacheResult();
+    instanceCacheResult.setAppId(appId);
+    instanceCacheResult.setMicroserviceName(microserviceName);
+    instanceCacheResult.setStatus(Status.NORMAL);
+    expectedSummary.getProducers().add(instanceCacheResult);
+
+    Assert.assertEquals(Json.encode(expectedSummary), 
Json.encode(instanceCacheSummary));
+  }
+
+  protected Holder<MicroserviceInstances> createFindServiceInstancesResult() {
+    MicroserviceInstances microserviceInstances = new MicroserviceInstances();
+    microserviceInstances.setNeedRefresh(true);
+    microserviceInstances.setRevision("first");
+    FindInstancesResponse findInstancesResponse = new FindInstancesResponse();
+    findInstancesResponse.setInstances(new ArrayList<>());
+    microserviceInstances.setInstancesResponse(findInstancesResponse);
+
+    Holder<MicroserviceInstances> findHolder = new Holder<>();
+    findHolder.value = microserviceInstances;
+    return findHolder;
+  }
+
+  protected void registerMicroservice(String appId, String microserviceName) {
+    Microservice microservice = new Microservice();
+    microservice.setAppId(appId);
+    microservice.setServiceName(microserviceName);
+    microservice.setVersion("1.0.0");
+
+    
serviceRegistry.getServiceRegistryClient().registerMicroservice(microservice);
+  }
+
+  @Test
+  public void check_findInstances_failed() {
+    Holder<MicroserviceInstances> findHolder = 
createFindServiceInstancesResult();
+
+    new MockUp<RegistryUtils>() {
+      @Mock
+      MicroserviceInstances findServiceInstances(String appId, String 
serviceName,
+          String versionRule, String revision) {
+        return findHolder.value;
+      }
+    };
+
+    registerMicroservice(appId, microserviceName);
+
+    serviceRegistry.getAppManager()
+        .getOrCreateMicroserviceVersionRule(appId, microserviceName, 
DefinitionConst.VERSION_RULE_ALL);
+
+    findHolder.value = null;
+    InstanceCacheSummary instanceCacheSummary = checker.check();
+
+    InstanceCacheResult instanceCacheResult = new InstanceCacheResult();
+    instanceCacheResult.setAppId(appId);
+    instanceCacheResult.setMicroserviceName(microserviceName);
+    instanceCacheResult.setStatus(Status.UNKNOWN);
+    instanceCacheResult.setDetail("failed to find instances from service 
center");
+    expectedSummary.getProducers().add(instanceCacheResult);
+    expectedSummary.setStatus(Status.UNKNOWN);
+
+    Assert.assertEquals(Json.encode(expectedSummary), 
Json.encode(instanceCacheSummary));
+  }
+
+  @Test
+  public void check_findInstances_serviceNotExist() {
+    Holder<MicroserviceInstances> findHolder = 
createFindServiceInstancesResult();
+
+    new MockUp<RegistryUtils>() {
+      @Mock
+      MicroserviceInstances findServiceInstances(String appId, String 
serviceName,
+          String versionRule, String revision) {
+        return findHolder.value;
+      }
+    };
+
+    registerMicroservice(appId, microserviceName);
+
+    serviceRegistry.getAppManager()
+        .getOrCreateMicroserviceVersionRule(appId, microserviceName, 
DefinitionConst.VERSION_RULE_ALL);
+
+    findHolder.value.setMicroserviceNotExist(true);
+    InstanceCacheSummary instanceCacheSummary = checker.check();
+
+    InstanceCacheResult instanceCacheResult = new InstanceCacheResult();
+    instanceCacheResult.setAppId(appId);
+    instanceCacheResult.setMicroserviceName(microserviceName);
+    instanceCacheResult.setStatus(Status.UNKNOWN);
+    instanceCacheResult.setDetail("microservice is not exist anymore, will be 
deleted from memory in next pull");
+    expectedSummary.getProducers().add(instanceCacheResult);
+    expectedSummary.setStatus(Status.UNKNOWN);
+
+    Assert.assertEquals(Json.encode(expectedSummary), 
Json.encode(instanceCacheSummary));
+  }
+
+  @Test
+  public void check_findInstances_revisionNotMatch() {
+    Holder<MicroserviceInstances> findHolder = 
createFindServiceInstancesResult();
+
+    new MockUp<RegistryUtils>() {
+      @Mock
+      MicroserviceInstances findServiceInstances(String appId, String 
serviceName,
+          String versionRule, String revision) {
+        return findHolder.value;
+      }
+    };
+
+    registerMicroservice(appId, microserviceName);
+
+    serviceRegistry.getAppManager()
+        .getOrCreateMicroserviceVersionRule(appId, microserviceName, 
DefinitionConst.VERSION_RULE_ALL);
+
+    findHolder.value.setRevision("second");
+    InstanceCacheSummary instanceCacheSummary = checker.check();
+
+    InstanceCacheResult instanceCacheResult = new InstanceCacheResult();
+    instanceCacheResult.setAppId(appId);
+    instanceCacheResult.setMicroserviceName(microserviceName);
+    instanceCacheResult.setStatus(Status.UNKNOWN);
+    instanceCacheResult.setDetail(
+        "revision is different, will be synchronized in next pull. local 
revision=first, remote revision=second");
+    expectedSummary.getProducers().add(instanceCacheResult);
+    expectedSummary.setStatus(Status.UNKNOWN);
+
+    Assert.assertEquals(Json.encode(expectedSummary), 
Json.encode(instanceCacheSummary));
+  }
+
+  @Test
+  public void check_findInstances_cacheNotMatch() {
+    Holder<MicroserviceInstances> findHolder = 
createFindServiceInstancesResult();
+
+    new MockUp<RegistryUtils>() {
+      @Mock
+      MicroserviceInstances findServiceInstances(String appId, String 
serviceName,
+          String versionRule, String revision) {
+        return findHolder.value;
+      }
+    };
+
+    registerMicroservice(appId, microserviceName);
+
+    serviceRegistry.getAppManager()
+        .getOrCreateMicroserviceVersionRule(appId, microserviceName, 
DefinitionConst.VERSION_RULE_ALL);
+
+    Holder<MicroserviceInstances> newFindHolder = 
createFindServiceInstancesResult();
+    newFindHolder.value.getInstancesResponse().getInstances().add(new 
MicroserviceInstance());
+    findHolder.value = newFindHolder.value;
+    InstanceCacheSummary instanceCacheSummary = checker.check();
+
+    InstanceCacheResult instanceCacheResult = new InstanceCacheResult();
+    instanceCacheResult.setAppId(appId);
+    instanceCacheResult.setMicroserviceName(microserviceName);
+    instanceCacheResult.setStatus(Status.ABNORMAL);
+    instanceCacheResult.setDetail(
+        "instance cache not match");
+    expectedSummary.getProducers().add(instanceCacheResult);
+    expectedSummary.setStatus(Status.ABNORMAL);
+
+    Assert.assertEquals(Json.encode(expectedSummary), 
Json.encode(instanceCacheSummary));
+  }
+}
diff --git 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java
 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java
index d4d2b5d15..3b01fe885 100644
--- 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java
+++ 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java
@@ -100,4 +100,20 @@ public void testSchema() {
     String content = 
serviceRegistry.getServiceRegistryClient().getSchema(microservice.getServiceId(),
 "s1");
     Assert.assertEquals("s1-content", content);
   }
+
+  @Test
+  public void registerMicroservice() {
+    ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal();
+    serviceRegistry.init();
+    serviceRegistry.run();
+    
+    Microservice microservice = new Microservice();
+    microservice.setAppId("appId");
+    microservice.setServiceName("msName");
+
+    String serviceId = 
serviceRegistry.getServiceRegistryClient().registerMicroservice(microservice);
+    Microservice remoteMicroservice = 
serviceRegistry.getRemoteMicroservice(serviceId);
+
+    Assert.assertEquals(serviceId, remoteMicroservice.getServiceId());
+  }
 }
diff --git 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java
 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java
index d46ef932f..73483fac1 100644
--- 
a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java
+++ 
b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java
@@ -17,12 +17,15 @@
 package org.apache.servicecomb.serviceregistry.registry;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 import org.apache.servicecomb.serviceregistry.RegistryUtils;
 import org.apache.servicecomb.serviceregistry.ServiceRegistry;
 import 
org.apache.servicecomb.serviceregistry.client.LocalServiceRegistryClientImpl;
@@ -65,12 +68,20 @@ protected ServiceRegistryClient 
createServiceRegistryClient() {
 
   @Test
   public void testLifeCycle(@Injectable ServiceRegistryConfig config, 
@Injectable MicroserviceDefinition definition,
-      @Injectable ServiceRegistry registry) {
+      @Injectable ServiceRegistry registry) throws InterruptedException {
     ArrayList<IpPort> ipPortList = new ArrayList<>();
     ipPortList.add(new IpPort("127.0.0.1", 9980));
     ipPortList.add(new IpPort("127.0.0.1", 9981));
 
-    new Expectations() {
+    CountDownLatch latch = new CountDownLatch(1);
+    ServiceRegistryTaskInitializer initializer = new 
MockUp<ServiceRegistryTaskInitializer>() {
+      @Mock
+      void init(RemoteServiceRegistry remoteServiceRegistry) {
+        latch.countDown();
+      }
+    }.getMockInstance();
+
+    new Expectations(SPIServiceUtils.class) {
       {
         definition.getConfiguration();
         result = ConfigUtil.createLocalConfig();
@@ -86,6 +97,8 @@ public void testLifeCycle(@Injectable ServiceRegistryConfig 
config, @Injectable
         result = 30;
         config.isWatch();
         result = false;
+        
SPIServiceUtils.getOrLoadSortedService(ServiceRegistryTaskInitializer.class);
+        result = Arrays.asList(initializer);
       }
     };
 
@@ -95,6 +108,10 @@ public void testLifeCycle(@Injectable ServiceRegistryConfig 
config, @Injectable
     RemoteServiceRegistry remote = new TestingRemoteServiceRegistry(bus, 
config, definition);
     remote.init();
     remote.run();
+
+    // should not block
+    latch.await();
+
     Assert.assertTrue(2 <= remote.getTaskPool().getTaskCount()); // includes 
complete tasks
 
     bus.post(new ShutdownEvent());
@@ -102,7 +119,6 @@ public void testLifeCycle(@Injectable ServiceRegistryConfig 
config, @Injectable
     remote.getTaskPool().schedule(new Runnable() {
       @Override
       public void run() {
-        // TODO Auto-generated method stub
 
       }
     }, 0, TimeUnit.SECONDS);


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to