[SCB-2361] Support SC dual cluster multi active mode (#2691)

diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/AddressManager.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/AddressManager.java
index 28772d4..b939657 100644
--- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/AddressManager.java
+++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/AddressManager.java
@@ -17,46 +17,28 @@
 
 package org.apache.servicecomb.config.center.client;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.lang.StringUtils;
-import org.apache.servicecomb.http.client.common.HttpUtils;
+import org.apache.servicecomb.http.client.common.AbstractAddressManager;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
 
-public class AddressManager {
-  public static final String DEFAULT_PROJECT = "default";
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
 
-  private final String projectName;
+public class AddressManager extends AbstractAddressManager {
 
-  private final List<String> addresses;
-
-  private int index = 0;
-
-  public AddressManager(String projectName, List<String> addresses) {
-    this.projectName = StringUtils.isEmpty(projectName) ? DEFAULT_PROJECT : projectName;
-    this.addresses = new ArrayList<>(addresses.size());
-    addresses.forEach((address -> this.addresses.add(formatAddress(address))));
+  public AddressManager(String projectName, List<String> addresses, EventBus eventBus) {
+    super(projectName, addresses);
+    eventBus.register(this);
   }
 
-  private String formatAddress(String address) {
-    try {
-      return address + "/v3/" + HttpUtils.encodeURLParam(this.projectName);
-    } catch (Exception e) {
-      throw new IllegalStateException("not possible");
-    }
+  @Override
+  public String joinProject(String address) {
+    return formatAddress(address);
   }
 
-  public String address() {
-    synchronized (this) {
-      this.index++;
-      if (this.index >= addresses.size()) {
-        this.index = 0;
-      }
-      return addresses.get(index);
-    }
+  @Subscribe
+  public void onRefreshEndpointEvent(RefreshEndpointEvent event) {
+    refreshEndpoint(event, RefreshEndpointEvent.CONFIG_CENTER_NAME);
   }
-
-  public boolean sslEnabled() {
-    return address().startsWith("https://");
-  }
-}
+}
\ No newline at end of file
diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java
index 132fe20..50d1ce5 100644
--- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java
+++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java
@@ -65,8 +65,9 @@
     Map<String, Object> configurations = new HashMap<>();
 
     String uri = null;
+    String address = addressManager.address();
     try {
-      uri = addressManager.address() + "/configuration/items?dimensionsInfo="
+      uri = address + "/configuration/items?dimensionsInfo="
           + HttpUtils.encodeURLParam(dimensionsInfo) + "&revision=" + request.getRevision();
 
       Map<String, String> headers = new HashMap<>();
@@ -102,13 +103,16 @@
         }
         queryConfigurationsResponse.setConfigurations(configurations);
         queryConfigurationsResponse.setChanged(true);
+        addressManager.recordSuccessState(address);
         return queryConfigurationsResponse;
       } else if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
         queryConfigurationsResponse.setChanged(false);
+        addressManager.recordSuccessState(address);
         return queryConfigurationsResponse;
       } else if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) {
         throw new OperationException("Bad request for query configurations.");
       } else {
+        addressManager.recordFailState(address);
         throw new OperationException(
             "read response failed. status:"
                 + httpResponse.getStatusCode()
@@ -118,6 +122,7 @@
                 + httpResponse.getContent());
       }
     } catch (IOException e) {
+      addressManager.recordFailState(address);
       LOGGER.error("query configuration from {} failed, message={}", uri, e.getMessage());
       throw new OperationException("", e);
     }
diff --git a/clients/config-kie-client/pom.xml b/clients/config-kie-client/pom.xml
index 198e306..b81d518 100644
--- a/clients/config-kie-client/pom.xml
+++ b/clients/config-kie-client/pom.xml
@@ -42,6 +42,12 @@
             <groupId>org.springframework</groupId>
             <artifactId>spring-beans</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>failureaccess</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 
diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java
index 5b64f2d..6a54aa6 100644
--- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java
+++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java
@@ -66,8 +66,8 @@
 
   @Override
   public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request) {
-    String url = buildUrl(request);
-
+    String address = addressManager.address();
+    String url = buildUrl(request, address);
     try {
       if (kieConfiguration.isEnableLongPolling()) {
         url += "&wait=" + kieConfiguration.getPollingWaitInSeconds() + "s";
@@ -83,6 +83,7 @@
         configurationsResponse.setConfigurations(configurations);
         configurationsResponse.setChanged(true);
         configurationsResponse.setRevision(revision);
+        addressManager.recordSuccessState(address);
         return configurationsResponse;
       }
       if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) {
@@ -90,8 +91,10 @@
       }
       if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
         configurationsResponse.setChanged(false);
+        addressManager.recordSuccessState(address);
         return configurationsResponse;
       }
+      addressManager.recordFailState(address);
       throw new OperationException(
           "read response failed. status:" + httpResponse.getStatusCode() + "; message:" +
               httpResponse.getMessage() + "; content:" + httpResponse.getContent());
@@ -101,9 +104,9 @@
     }
   }
 
-  private String buildUrl(ConfigurationsRequest request) {
+  private String buildUrl(ConfigurationsRequest request, String currentAddress) {
     StringBuilder sb = new StringBuilder();
-    sb.append(addressManager.address());
+    sb.append(currentAddress);
     sb.append("/");
     sb.append(DEFAULT_KIE_API_VERSION);
     sb.append("/");
diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java
index dde884f..0742c11 100644
--- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java
+++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java
@@ -17,30 +17,23 @@
 
 package org.apache.servicecomb.config.kie.client.model;
 
-import java.util.ArrayList;
 import java.util.List;
 
-public class KieAddressManager {
-  private final List<String> addresses;
+import org.apache.servicecomb.http.client.common.AbstractAddressManager;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
 
-  private int index = 0;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
 
-  public KieAddressManager(List<String> addresses) {
-    this.addresses = new ArrayList<>(addresses.size());
-    this.addresses.addAll(addresses);
-  }
-  
-  public String address() {
-    synchronized (this) {
-      this.index++;
-      if (this.index >= addresses.size()) {
-        this.index = 0;
-      }
-      return addresses.get(index);
-    }
+public class KieAddressManager extends AbstractAddressManager {
+
+  public KieAddressManager(List<String> addresses, EventBus eventBus) {
+    super(addresses);
+    eventBus.register(this);
   }
 
-  public boolean sslEnabled() {
-    return address().startsWith("https://");
+  @Subscribe
+  public void onRefreshEndpointEvent(RefreshEndpointEvent event) {
+    refreshEndpoint(event, RefreshEndpointEvent.KIE_NAME);
   }
 }
diff --git a/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java
new file mode 100644
index 0000000..2269f6d
--- /dev/null
+++ b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.config.kie.client.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.eventbus.EventBus;
+
+import mockit.Deencapsulation;
+
+class KieAddressManagerTest {
+
+  private static List<String> addresses = new ArrayList<>();
+
+  private static KieAddressManager addressManager1;
+
+  @Test
+  public void kieAddressManagerTest() {
+    addresses.add("http://127.0.0.1:30103");
+    addresses.add("https://127.0.0.2:30103");
+    addressManager1 = new KieAddressManager(addresses, new EventBus());
+
+    Assert.assertNotNull(addressManager1);
+
+    List<String> addresses = Deencapsulation.getField(addressManager1, "addresses");
+    Assert.assertEquals(2, addresses.size());
+    Assert.assertEquals("http://127.0.0.1:30103", addresses.get(0));
+
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103", addressManager1.address());
+  }
+
+
+  @Test
+  public void onRefreshEndpointEvent() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("http://127.0.0.3:30100");
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("http://127.0.0.4:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", addressRG);
+    addressManager1 = new KieAddressManager(addresses, new EventBus());
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "KIE");
+    addressManager1.refreshEndpoint(event, "KIE");
+
+    List<String> availableZone = Deencapsulation.getField(addressManager1, "availableZone");
+    Assert.assertEquals("http://127.0.0.3:30100", availableZone.get(0));
+
+    List<String> availableRegion = Deencapsulation.getField(addressManager1, "availableRegion");
+    Assert.assertEquals("http://127.0.0.4:30100", availableRegion.get(0));
+  }
+}
\ No newline at end of file
diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java
new file mode 100644
index 0000000..971fc42
--- /dev/null
+++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java
@@ -0,0 +1,309 @@
+/*
+ * 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.http.client.common;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+public class AbstractAddressManager {
+  private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAddressManager.class);
+
+  public static final String DEFAULT_PROJECT = "default";
+
+  public static final String V4_PREFIX = "/v4/";
+
+  private static final String V3_PREFIX = "/v3/";
+
+  private static final int DEFAULT_METRICS_WINDOW_TIME = 1;
+
+  private List<String> addresses = new ArrayList<>();
+
+  private int index = 0;
+
+  private String projectName;
+
+  private Map<String, Boolean> categoryMap = new HashMap<>();
+
+  private Map<String, Integer> recodeStatus = new ConcurrentHashMap<>();
+
+  private Map<String, Boolean> history = new ConcurrentHashMap<>();
+
+  private volatile List<String> availableZone = new ArrayList<>();
+
+  private volatile List<String> availableRegion = new ArrayList<>();
+
+  private volatile List<String> defaultAddress = new ArrayList<>();
+
+  private boolean isAddressRefresh = false;
+
+  private final Object lock = new Object();
+
+  private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1,
+      new ThreadFactoryBuilder()
+          .setNameFormat("check-available-address-%d")
+          .build());
+
+  private Cache<String, Boolean> cacheAddress = CacheBuilder.newBuilder()
+      .maximumSize(100)
+      .expireAfterWrite(10, TimeUnit.MINUTES)
+      .build();
+
+  public AbstractAddressManager(List<String> addresses) {
+    this.addresses.addAll(addresses);
+  }
+
+  public AbstractAddressManager(String projectName, List<String> addresses) {
+    this.projectName = StringUtils.isEmpty(projectName) ? DEFAULT_PROJECT : projectName;
+    this.addresses = this.transformAddress(addresses);
+    this.defaultAddress = this.addresses;
+  }
+
+  @VisibleForTesting
+  protected void setAddressRefresh(boolean addressRefresh) {
+    isAddressRefresh = addressRefresh;
+  }
+
+  private void startCheck() {
+    executorService.scheduleAtFixedRate(this::checkHistory,
+        0,
+        DEFAULT_METRICS_WINDOW_TIME,
+        TimeUnit.MINUTES);
+  }
+
+  public String formatUrl(String url, boolean absoluteUrl, String address) {
+    return absoluteUrl ? address + url : formatAddress(address) + url;
+  }
+
+  // if isAddressRefresh is false, polling with available initial addresses.
+  public String address() {
+    if (!isAddressRefresh) {
+      return getDefaultAddress();
+    } else {
+      return getAvailableZoneAddress();
+    }
+  }
+
+  public boolean sslEnabled() {
+    return address().startsWith("https://");
+  }
+
+  protected List<String> transformAddress(List<String> addresses) {
+    return addresses.stream().map(this::formatAddress).collect(Collectors.toList());
+  }
+
+  protected String getUrlPrefix(String address) {
+    return address + V3_PREFIX;
+  }
+
+  protected String formatAddress(String address) {
+    try {
+      return getUrlPrefix(address) + HttpUtils.encodeURLParam(this.projectName);
+    } catch (Exception e) {
+      throw new IllegalStateException("not possible");
+    }
+  }
+
+  private String getDefaultAddress() {
+    List<String> addresses = getAvailableAddress(defaultAddress);
+    if (!addresses.isEmpty()) {
+      return getCurrentAddress(addresses);
+    }
+    return getInitAddress();
+  }
+
+  private String getAvailableZoneAddress() {
+    List<String> addresses = getAvailableZoneIpPorts();
+    if (!addresses.isEmpty()) {
+      return joinProject(getCurrentAddress(addresses));
+    }
+    return getInitAddress();
+  }
+
+  // when all available address is fail, it will use all the initial addresses for polling.
+  private String getInitAddress() {
+    if (addresses.isEmpty()) {
+      return null;
+    }
+    return getCurrentAddress(addresses);
+  }
+
+  private String getCurrentAddress(List<String> addresses) {
+    synchronized (this) {
+      this.index++;
+      if (this.index >= addresses.size()) {
+        this.index = 0;
+      }
+      return addresses.get(index);
+    }
+  }
+
+  protected String joinProject(String address) {
+    return address;
+  }
+
+  private List<String> getAvailableZoneIpPorts() {
+    List<String> results = new ArrayList<>();
+    if (!availableZone.isEmpty()) {
+      results.addAll(getAvailableAddress(availableZone));
+    } else {
+      results.addAll(getAvailableAddress(availableRegion));
+    }
+    return results;
+  }
+
+  private List<String> getAvailableAddress(List<String> endpoints) {
+    List<String> list = endpoints.stream().filter(uri -> !history.containsKey(uri))
+        .collect(Collectors.toList());
+    return list;
+  }
+
+  protected String normalizeUri(String endpoint) {
+    if (endpoint.contains("sslEnabled=true")) {
+      return StringUtils.replace(endpoint, "rest", "https");
+    }
+    return StringUtils.replace(endpoint, "rest", "http");
+  }
+
+  public void refreshEndpoint(RefreshEndpointEvent event, String key) {
+    this.setAddressRefresh(true);
+    if (null == event || !event.getName().equals(key)) {
+      return;
+    }
+    availableZone = event.getSameZone().stream().map(this::normalizeUri).collect(Collectors.toList());
+    availableRegion = event.getSameRegion().stream().map(this::normalizeUri).collect(Collectors.toList());
+    availableZone.forEach(address -> categoryMap.put(address, true));
+    availableRegion.forEach(address -> categoryMap.put(address, false));
+    startCheck();
+  }
+
+  public void recordFailState(String address) {
+    if (!recodeStatus.containsKey(address)) {
+      recodeStatus.put(address, 1);
+      return;
+    }
+    synchronized (lock) {
+      int number = recodeStatus.get(address) + 1;
+      if (number < 3) {
+        recodeStatus.put(address, number);
+      } else {
+        removeAddress(address);
+      }
+    }
+  }
+
+  public void recordSuccessState(String address) {
+    recodeStatus.put(address, 0);
+  }
+
+  @VisibleForTesting
+  protected void checkHistory() {
+    history.keySet().stream().filter(this::judgeIsolation).forEach(s -> {
+      if (telnetTest(s)) {
+        rejoinAddress(s);
+      } else {
+        cacheAddress.put(s, false);
+      }
+    });
+  }
+
+  private Boolean judgeIsolation(String address) {
+    try {
+      return cacheAddress.get(address, () -> true);
+    } catch (ExecutionException e) {
+      return true;
+    }
+  }
+
+  private boolean telnetTest(String address) {
+    URI ipPort = parseIpPortFromURI(address);
+    try (Socket s = new Socket()) {
+      s.connect(new InetSocketAddress(ipPort.getHost(), ipPort.getPort()), 3000);
+      return true;
+    } catch (IOException e) {
+      LOGGER.warn("ping endpoint {} failed, It will be quarantined again.", address);
+    }
+    return false;
+  }
+
+  private URI parseIpPortFromURI(String uri) {
+    try {
+      return new URI(uri);
+    } catch (URISyntaxException e) {
+      return null;
+    }
+  }
+
+  //Query whether the current address belongs to the same AZ or region through azmap,
+  // add it to the sequence of, and delete the record in history
+  @VisibleForTesting
+  void rejoinAddress(String address) {
+    if (!isAddressRefresh) {
+      defaultAddress.add(address);
+    } else {
+      if (categoryMap.get(address)) {
+        availableZone.add(address);
+      } else {
+        availableRegion.add(address);
+      }
+    }
+    recodeStatus.put(address, 0);
+    history.remove(address);
+  }
+
+  //Query whether the current address belongs to the same AZ or the same region through AZMap,
+  // and delete it from the record. At the same time, add records in history and cache
+  @VisibleForTesting
+  void removeAddress(String address) {
+    if (!isAddressRefresh) {
+      defaultAddress.remove(address);
+      history.put(address, false);
+    } else {
+      if (categoryMap.get(address)) {
+        availableZone.remove(address);
+      } else {
+        availableRegion.remove(address);
+      }
+      history.put(address, categoryMap.get(address));
+    }
+    recodeStatus.put(address, 0);
+    cacheAddress.put(address, false);
+  }
+}
diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/RefreshEndpointEvent.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/RefreshEndpointEvent.java
new file mode 100644
index 0000000..4e4f1be
--- /dev/null
+++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/RefreshEndpointEvent.java
@@ -0,0 +1,77 @@
+/*
+ * 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.http.client.event;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RefreshEndpointEvent {
+
+  public static final String SERVICE_CENTER_NAME = "SERVICECENTER";
+
+  public static final String KIE_NAME = "KIE";
+
+  public static final String CONFIG_CENTER_NAME = "CseConfigCenter";
+
+  public static final String CSE_MONITORING_NAME = "CseMonitoring";
+
+  private static final String SAME_ZONE = "sameZone";
+
+  private static final String SAME_REGION = "sameRegion";
+
+  private Map<String, List<String>> zoneAndRegion = new HashMap<>();
+
+  private String name;
+
+  public RefreshEndpointEvent(Map<String, List<String>> zoneAndRegion, String name) {
+    this.zoneAndRegion = zoneAndRegion;
+    this.name = name;
+  }
+
+  public List<String> getSameZone() {
+    if (zoneAndRegion.get(SAME_ZONE).isEmpty()) {
+      return new ArrayList<>();
+    }
+    return zoneAndRegion.get(SAME_ZONE);
+  }
+
+  public List<String> getSameRegion() {
+    if (zoneAndRegion.get(SAME_REGION).isEmpty()) {
+      return new ArrayList<>();
+    }
+    return zoneAndRegion.get(SAME_REGION);
+  }
+
+  public Map<String, List<String>> getZoneAndRegion() {
+    return zoneAndRegion;
+  }
+
+  public void setZoneAndRegion(Map<String, List<String>> zoneAndRegion) {
+    this.zoneAndRegion = zoneAndRegion;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+}
diff --git a/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java
new file mode 100644
index 0000000..ee38ced
--- /dev/null
+++ b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java
@@ -0,0 +1,299 @@
+/*
+ * 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.http.client.common;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.junit.Assert;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+
+import mockit.Deencapsulation;
+import mockit.Expectations;
+
+public class AbstractAddressManagerTest {
+
+  private static List<String> addresses = new ArrayList<>();
+
+  private static AbstractAddressManager addressManager1;
+
+  private static AbstractAddressManager addressManager2;
+
+  private static AbstractAddressManager addressManager3;
+
+  @BeforeEach
+  public void setUp() {
+    addresses.add("http://127.0.0.1:30103");
+    addresses.add("https://127.0.0.2:30103");
+    addressManager1 = new AbstractAddressManager(addresses);
+    addressManager2 = new AbstractAddressManager("project", addresses);
+    addressManager3 = new AbstractAddressManager(null, addresses);
+  }
+
+  @AfterEach
+  public void tearDown() {
+    addresses.clear();
+    addressManager1 = null;
+    addressManager2 = null;
+    addressManager3 = null;
+  }
+
+  @Test
+  public void abstractAddressManagerTest() {
+    Assert.assertNotNull(addressManager1);
+    Assert.assertNotNull(addressManager2);
+    Assert.assertNotNull(addressManager3);
+
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103", addressManager1.address());
+  }
+
+  @Test
+  public void recordStateTest() throws ExecutionException {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("http://127.0.0.3:30100");
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("http://127.0.0.4:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", addressRG);
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST");
+    AbstractAddressManager addressManager = new AbstractAddressManager(addresses);
+
+    addressManager.refreshEndpoint(event, "TEST");
+
+    String address = "http://127.0.0.3:30100";
+    addressManager.recordFailState(address);
+
+    Assert.assertEquals("http://127.0.0.3:30100", addressManager.address());
+
+    addressManager.recordFailState(address);
+    Assert.assertEquals("http://127.0.0.3:30100", addressManager.address());
+
+    // test fail 2 times ,it will not be isolated
+    addressManager.recordSuccessState(address);
+    Assert.assertEquals("http://127.0.0.3:30100", addressManager.address());
+
+    // test recodeStatus times
+    Map<String, Integer> recodeStatus = Deencapsulation.getField(addressManager, "recodeStatus");
+    Assert.assertEquals(0, (int) recodeStatus.get("http://127.0.0.3:30100"));
+
+    // test fail 3 times ,it will be isolated
+    addressManager.recordFailState(address);
+    addressManager.recordFailState(address);
+    addressManager.recordFailState(address);
+    Assert.assertEquals("http://127.0.0.4:30100", addressManager.address());
+
+    // mock cacheAddress status refresh after 10 minute
+    Cache<String, Boolean> cache = CacheBuilder.newBuilder()
+        .maximumSize(100)
+        .expireAfterWrite(10, TimeUnit.MINUTES)
+        .build();
+    cache.put("http://127.0.0.3:30100", true);
+
+    // mock the address telnetTest is access
+    new Expectations(addressManager) {
+      {
+        Deencapsulation.setField(addressManager, "cacheAddress", cache);
+        Deencapsulation.invoke(addressManager, "telnetTest", "http://127.0.0.3:30100");
+        result = true;
+      }
+    };
+    Cache<String, Boolean> result = Deencapsulation.getField(addressManager, "cacheAddress");
+    Assert.assertEquals(true, result.get("http://127.0.0.3:30100", () -> false));
+
+    // test restore isolation
+    addressManager.checkHistory();
+    addressManager.rejoinAddress("http://127.0.0.3:30100");
+    Assert.assertEquals("http://127.0.0.3:30100", addressManager.address());
+    Assert.assertEquals("http://127.0.0.3:30100", addressManager.address());
+  }
+
+
+  @Test
+  public void testMiltiThread() throws Exception {
+
+    AbstractAddressManager addressManager = new AbstractAddressManager(addresses);
+    String address = "http://127.0.0.3:30100";
+
+    CountDownLatch latch = new CountDownLatch(2);
+    for (int i = 0; i < 2; i++) {
+      new Thread(() -> {
+        addressManager.recordFailState(address);
+        latch.countDown();
+      }).start();
+    }
+    latch.await(30, TimeUnit.SECONDS);
+
+    Map<String, Integer> recodeStatus = Deencapsulation.getField(addressManager, "recodeStatus");
+    Assert.assertEquals(2, (int) recodeStatus.get("http://127.0.0.3:30100"));
+  }
+
+  @Test
+  public void addressForOnlyDefaultTest() {
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103", addressManager1.address());
+
+    Assert.assertEquals("https://127.0.0.2:30103/v3/project", addressManager2.address());
+    Assert.assertEquals("http://127.0.0.1:30103/v3/project", addressManager2.address());
+
+    Assert.assertEquals("https://127.0.0.2:30103/v3/default", addressManager3.address());
+    Assert.assertEquals("http://127.0.0.1:30103/v3/default", addressManager3.address());
+  }
+
+  @Test
+  public void addressForOnlyAzTest() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("http://127.0.0.1:30100");
+    addressAZ.add("https://127.0.0.2:30100");
+    addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true");
+    addressAZ.add("rest://127.0.0.2:30100");
+
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", new ArrayList<>());
+    RefreshEndpointEvent event1 = new RefreshEndpointEvent(zoneAndRegion, "TEST");
+    addressManager1.refreshEndpoint(event1, "TEST");
+
+    Assert.assertEquals("https://127.0.0.2:30100", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.1:30100?sslEnabled=true", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.2:30100", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30100", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.2:30100", addressManager1.address());
+  }
+
+  @Test
+  public void addressForOnlyRegionTest() {
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("rest://127.0.0.5:30100?sslEnabled=true");
+    addressRG.add("rest://127.0.0.6:30100");
+    addressRG.add("http://127.0.0.7:30100");
+    addressRG.add("https://127.0.0.8:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", new ArrayList<>());
+    zoneAndRegion.put("sameRegion", addressRG);
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST");
+    addressManager1.refreshEndpoint(event, "TEST");
+
+    Assert.assertEquals("http://127.0.0.6:30100", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.7:30100", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.8:30100", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.5:30100?sslEnabled=true", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.6:30100", addressManager1.address());
+  }
+
+  @Test
+  public void addressForAzAndRegionTest() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true");
+    addressAZ.add("https://127.0.0.2:30100");
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("rest://127.0.0.3:30100?sslEnabled=true");
+    addressRG.add("https://127.0.0.4:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", addressRG);
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST");
+    addressManager1.refreshEndpoint(event, "TEST");
+
+    Assert.assertEquals("https://127.0.0.2:30100", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.1:30100?sslEnabled=true", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.2:30100", addressManager1.address());
+
+    addressManager1.removeAddress("https://127.0.0.2:30100");
+    addressManager1.removeAddress("https://127.0.0.1:30100?sslEnabled=true");
+    Assert.assertEquals("https://127.0.0.3:30100?sslEnabled=true", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.4:30100", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.3:30100?sslEnabled=true", addressManager1.address());
+
+    addressManager1.removeAddress("https://127.0.0.4:30100");
+    addressManager1.removeAddress("https://127.0.0.3:30100?sslEnabled=true");
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+  }
+
+  @Test
+  public void sslEnabledTest() {
+    Assert.assertEquals(true, addressManager1.sslEnabled());
+    Assert.assertEquals(false, addressManager1.sslEnabled());
+    Assert.assertEquals(true, addressManager1.sslEnabled());
+
+    Assert.assertEquals(true, addressManager2.sslEnabled());
+    Assert.assertEquals(false, addressManager2.sslEnabled());
+    Assert.assertEquals(true, addressManager2.sslEnabled());
+  }
+
+  @Test
+  public void transformAddressTest() {
+    List<String> address = new ArrayList<>();
+    address.add("rest://127.0.0.1:30100?sslEnabled=true");
+    address.add("rest://127.0.0.2:30100");
+    address.add("http://127.0.0.3:30100");
+    address.add("https://127.0.0.4:30100");
+
+    List<String> formAddress = addressManager2.transformAddress(address);
+
+    Assert.assertEquals("rest://127.0.0.1:30100?sslEnabled=true/v3/project", formAddress.get(0));
+    Assert.assertEquals("rest://127.0.0.2:30100/v3/project", formAddress.get(1));
+    Assert.assertEquals("http://127.0.0.3:30100/v3/project", formAddress.get(2));
+    Assert.assertEquals("https://127.0.0.4:30100/v3/project", formAddress.get(3));
+
+    formAddress = addressManager3.transformAddress(address);
+    Assert.assertEquals("rest://127.0.0.1:30100?sslEnabled=true/v3/default", formAddress.get(0));
+    Assert.assertEquals("rest://127.0.0.2:30100/v3/default", formAddress.get(1));
+    Assert.assertEquals("http://127.0.0.3:30100/v3/default", formAddress.get(2));
+    Assert.assertEquals("https://127.0.0.4:30100/v3/default", formAddress.get(3));
+  }
+
+  @Test
+  public void getUrlPrefixTest() {
+    Assert.assertEquals("http://127.0.0.3:30100/v3/", addressManager2.getUrlPrefix("http://127.0.0.3:30100"));
+    Assert.assertEquals("http://127.0.0.3:30100/v3/", addressManager3.getUrlPrefix("http://127.0.0.3:30100"));
+  }
+
+  @Test
+  public void refreshEndpointTest() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("rest://127.0.0.1:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", new ArrayList<>());
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST");
+
+    addressManager1.refreshEndpoint(event, "KIE");
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+
+    addressManager2.refreshEndpoint(event, "TEST");
+    Assert.assertEquals("http://127.0.0.1:30100", addressManager2.address());
+    Assert.assertEquals("http://127.0.0.1:30100", addressManager2.address());
+  }
+}
\ No newline at end of file
diff --git a/clients/service-center-client/pom.xml b/clients/service-center-client/pom.xml
index e2cbea8..ec3696d 100755
--- a/clients/service-center-client/pom.xml
+++ b/clients/service-center-client/pom.xml
@@ -48,5 +48,10 @@
       <artifactId>mockito-core</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>failureaccess</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
\ No newline at end of file
diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/AddressManager.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/AddressManager.java
index df3e96c..1185fde 100644
--- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/AddressManager.java
+++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/AddressManager.java
@@ -17,47 +17,32 @@
 
 package org.apache.servicecomb.service.center.client;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.servicecomb.http.client.common.HttpUtils;
+import org.apache.servicecomb.http.client.common.AbstractAddressManager;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
 
-public class AddressManager {
-  private final String projectName;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
 
-  private final List<String> addresses;
-
-  private int index = 0;
-
-  public AddressManager(String projectName, List<String> addresses) {
-    this.projectName = projectName;
-    this.addresses = new ArrayList<>(addresses.size());
-    this.addresses.addAll(addresses);
+public class AddressManager extends AbstractAddressManager {
+  public AddressManager(String projectName, List<String> addresses, EventBus eventBus) {
+    super(projectName, addresses);
+    eventBus.register(this);
   }
 
-  private String formatAddress(String address) {
-    try {
-      return address + "/v4/" + HttpUtils.encodeURLParam(this.projectName);
-    } catch (Exception e) {
-      throw new IllegalStateException("not possible");
-    }
+  @Override
+  protected List<String> transformAddress(List<String> addresses) {
+    return addresses;
   }
 
-  public boolean sslEnabled() {
-    return address().startsWith("https://");
+  @Override
+  protected String getUrlPrefix(String address) {
+    return address + V4_PREFIX;
   }
 
-  public String address() {
-    synchronized (this) {
-      index++;
-      if (index >= addresses.size()) {
-        index = 0;
-      }
-      return addresses.get(index);
-    }
-  }
-
-  public String formatUrl(String url, boolean absoluteUrl) {
-    return absoluteUrl ? address() + url : formatAddress(address()) + url;
+  @Subscribe
+  public void onRefreshEndpointEvent(RefreshEndpointEvent event) {
+    refreshEndpoint(event, RefreshEndpointEvent.SERVICE_CENTER_NAME);
   }
 }
diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java
index 3b9a48b..8f49df3 100755
--- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java
+++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java
@@ -69,24 +69,28 @@
   private HttpResponse doHttpRequest(String url, boolean absoluteUrl, Map<String, String> headers, String content,

       String method)

       throws IOException {

-

-    String address = addressManager.formatUrl(url, absoluteUrl);

+    String address = addressManager.address();

+    String formatUrl = addressManager.formatUrl(url, absoluteUrl, address);

     if (headers == null) {

       headers = new HashMap<>();

     }

     headers.put(HEADER_TENANT_NAME, tenantName);

-    HttpRequest httpRequest = new HttpRequest(address, headers, content, method);

+    HttpRequest httpRequest = new HttpRequest(formatUrl, headers, content, method);

 

     try {

-      return httpTransport.doRequest(httpRequest);

+      HttpResponse httpResponse = httpTransport.doRequest(httpRequest);

+      addressManager.recordSuccessState(address);

+      return httpResponse;

     } catch (IOException e) {

-      String retryAddress = addressManager.formatUrl(url, absoluteUrl);

-      LOGGER.warn("send request to {} failed and retry to {} once. ", address,

-          retryAddress, e);

-      httpRequest = new HttpRequest(retryAddress, headers, content, method);

+      addressManager.recordFailState(address);

+      String retryAddress = addressManager.address();

+      formatUrl = addressManager.formatUrl(url, absoluteUrl, retryAddress);

+      LOGGER.warn("send request to {} failed and retry to {} once. ", address, retryAddress, e);

+      httpRequest = new HttpRequest(formatUrl, headers, content, method);

       try {

         return httpTransport.doRequest(httpRequest);

       } catch (IOException ioException) {

+        addressManager.recordFailState(retryAddress);

         LOGGER.warn("retry to {} failed again. ", retryAddress, e);

         throw ioException;

       }

diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java
index d8faafa..597f516 100644
--- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java
+++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java
@@ -102,25 +102,26 @@
   private void startWatch() {
     connector.submit(() -> {
       backOff();
-
+      String address = addressManager.address();
       try {
         Map<String, String> headers = new HashMap<>();
         headers.put("x-domain-name", this.tenantName);
         headers.putAll(this.extraGlobalHeaders);
         headers.putAll(this.requestAuthHeaderProvider.loadAuthHeader(null));
-        currentServerUri = convertAddress();
+        currentServerUri = convertAddress(address);
         LOGGER.info("start watch to address {}", currentServerUri);
         webSocketTransport = new WebSocketTransport(currentServerUri, sslProperties,
             headers, this);
         webSocketTransport.connectBlocking();
+        addressManager.recordSuccessState(address);
       } catch (Exception e) {
+        addressManager.recordFailState(address);
         LOGGER.error("start watch failed. ", e);
       }
     });
   }
 
-  private String convertAddress() {
-    String address = addressManager.address();
+  private String convertAddress(String address) {
     String url = String.format(WATCH, project, serviceId);
     if (address.startsWith(HTTP)) {
       return WS + address.substring(HTTP.length()) + url;
@@ -129,7 +130,6 @@
     if (address.startsWith(HTTPS)) {
       return WSS + address.substring(HTTPS.length()) + url;
     }
-
     return address + url;
   }
 
diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/AddressManagerTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/AddressManagerTest.java
new file mode 100644
index 0000000..8dd0906
--- /dev/null
+++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/AddressManagerTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.service.center.client;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.eventbus.EventBus;
+
+import mockit.Deencapsulation;
+
+class AddressManagerTest {
+
+  private static List<String> addresses = new ArrayList<>();
+
+  private static AddressManager addressManager1;
+
+  private static AddressManager addressManager2;
+
+
+  @Test
+  public void getUrlPrefix() {
+    addresses.add("http://127.0.0.1:30103");
+    addressManager1 = new AddressManager("project", addresses, new EventBus());
+
+    Assert.assertNotNull(addressManager1);
+
+    List<String> addresses = Deencapsulation.getField(addressManager1, "addresses");
+    Assert.assertEquals(1, addresses.size());
+    Assert.assertEquals("http://127.0.0.1:30103", addresses.get(0));
+    Assert.assertEquals("http://127.0.0.1:30103", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103/v4/", addressManager1.getUrlPrefix("http://127.0.0.1:30103"));
+  }
+
+  @Test
+  public void formatUrlTest() {
+    addresses.add("http://127.0.0.1:30103");
+    addressManager1 = new AddressManager("project", addresses, new EventBus());
+    Assert.assertNotNull(addressManager1);
+
+    String address = addressManager1.address();
+    Assert.assertEquals("http://127.0.0.1:30103", address);
+    String url = addressManager1.formatUrl("/test/", false, address);
+    Assert.assertEquals("http://127.0.0.1:30103/v4/project/test/", url);
+
+    url = addressManager1.formatUrl("/test/", true, address);
+    Assert.assertEquals("http://127.0.0.1:30103/test/", url);
+  }
+
+  @Test
+  public void onRefreshEndpointEvent() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("http://127.0.0.3:30100");
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("http://127.0.0.4:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", addressRG);
+    addressManager1 = new AddressManager("project", addresses, new EventBus());
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "SERVICECENTER");
+    addressManager1.refreshEndpoint(event, "SERVICECENTER");
+
+    List<String> availableZone = Deencapsulation.getField(addressManager1, "availableZone");
+    Assert.assertEquals("http://127.0.0.3:30100", availableZone.get(0));
+
+    List<String> availableRegion = Deencapsulation.getField(addressManager1, "availableRegion");
+    Assert.assertEquals("http://127.0.0.4:30100", availableRegion.get(0));
+  }
+}
\ No newline at end of file
diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java
index f7a3f7b..0bdf026 100755
--- a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java
+++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java
@@ -26,6 +26,8 @@
 import org.junit.Test;

 import org.mockito.Mockito;

 

+import com.google.common.eventbus.EventBus;

+

 /**

  * Created by   on 2019/10/16.

  */

@@ -43,8 +45,7 @@
   public void TestDefaultParameter() throws IOException {

 

     HttpTransport httpTransport = Mockito.mock(HttpTransport.class);

-

-    AddressManager addressManager = new AddressManager(PROJECT_NAME, Arrays.asList("http://127.0.0.1:30100"));

+    AddressManager addressManager = new AddressManager(PROJECT_NAME, Arrays.asList("http://127.0.0.1:30100"), new EventBus());

     ServiceCenterRawClient client = new ServiceCenterRawClient.Builder()

         .setHttpTransport(httpTransport)

         .setAddressManager(addressManager)

diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java
index ef523b2..3f54cfe 100644
--- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java
+++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java
@@ -65,7 +65,7 @@
 
   @Override
   public void testRestTransport() throws Exception {
-    AddressManager addressManager = new AddressManager("default", Arrays.asList("http://127.0.0.1:30100"));
+    AddressManager addressManager = new AddressManager("default", Arrays.asList("http://127.0.0.1:30100"), new EventBus());
     SSLProperties sslProperties = new SSLProperties();
     sslProperties.setEnabled(false);
     ServiceCenterClient serviceCenterClient = new ServiceCenterClient(addressManager, sslProperties,
diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImpl.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImpl.java
index 91059b8..ee94138 100644
--- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImpl.java
+++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImpl.java
@@ -180,7 +180,8 @@
   private AddressManager configKieAddressManager() {
     return new AddressManager(ConfigCenterConfig.INSTANCE.getDomainName(),
         Deployment
-            .getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER).getAccessURL());
+            .getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER).getAccessURL(),
+        EventManager.getEventBus());
   }
 
   private void updateConfiguration(WatchedUpdateResult result) {
diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImplTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImplTest.java
new file mode 100644
index 0000000..c53d41d
--- /dev/null
+++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImplTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.config;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.servicecomb.config.center.client.AddressManager;
+import org.apache.servicecomb.foundation.common.event.EventManager;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+class ConfigCenterConfigurationSourceImplTest {
+
+  @Test
+  void configKieAddressManagerTest() {
+    List<String> addresses = new ArrayList<>();
+    addresses.add("http://127.0.0.1:30103");
+    addresses.add("http://127.0.0.2:30103");
+    AddressManager addressManager = new AddressManager("test", addresses, EventManager.getEventBus());
+    Assert.assertNotNull(addressManager);
+
+    String address = addressManager.address();
+    Assert.assertEquals("http://127.0.0.2:30103/v3/test",address);
+    address = addressManager.address();
+    Assert.assertEquals("http://127.0.0.1:30103/v3/test",address);
+
+    addressManager = new AddressManager(null, addresses, EventManager.getEventBus());
+    address = addressManager.address();
+    Assert.assertEquals("http://127.0.0.2:30103/v3/default",address);
+  }
+}
\ No newline at end of file
diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/center/client/AddressManagerTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/center/client/AddressManagerTest.java
new file mode 100644
index 0000000..a0c4228
--- /dev/null
+++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/center/client/AddressManagerTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.config.center.client;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.eventbus.EventBus;
+
+import mockit.Deencapsulation;
+
+class AddressManagerTest {
+  private static List<String> addresses = new ArrayList<>();
+
+  private static AddressManager addressManager1;
+
+  private static AddressManager addressManager2;
+
+  @Test
+  public void addressManagerTest() {
+    addresses.add("http://127.0.0.1:30103");
+    addresses.add("https://127.0.0.2:30103");
+    addressManager1 = new AddressManager("project", addresses, new EventBus());
+    addressManager2 = new AddressManager(null, addresses, new EventBus());
+
+    Assert.assertNotNull(addressManager1);
+    Assert.assertNotNull(addressManager2);
+
+    List<String> addresses = Deencapsulation.getField(addressManager1, "addresses");
+    Assert.assertEquals(2, addresses.size());
+    Assert.assertEquals("http://127.0.0.1:30103/v3/project", addresses.get(0));
+
+    Assert.assertEquals("https://127.0.0.2:30103/v3/project", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103/v3/project", addressManager1.address());
+    Assert.assertEquals("https://127.0.0.2:30103/v3/default", addressManager2.address());
+  }
+
+  @Test
+  public void onRefreshEndpointEvent() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("http://127.0.0.3:30100");
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("http://127.0.0.4:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", addressRG);
+    addressManager1 = new AddressManager("project", addresses, new EventBus());
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter");
+    addressManager1.refreshEndpoint(event, "CseConfigCenter");
+
+    List<String> availableZone = Deencapsulation.getField(addressManager1, "availableZone");
+    Assert.assertEquals("http://127.0.0.3:30100", availableZone.get(0));
+
+    List<String> availableRegion = Deencapsulation.getField(addressManager1, "availableRegion");
+    Assert.assertEquals("http://127.0.0.4:30100", availableRegion.get(0));
+  }
+}
\ No newline at end of file
diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigurationSourceImpl.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigurationSourceImpl.java
index cd06a82..183bd6a 100644
--- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigurationSourceImpl.java
+++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigurationSourceImpl.java
@@ -131,7 +131,7 @@
       HttpClientBuilder httpClientBuilder = HttpClientBuilder.create().
           setDefaultRequestConfig(requestConfig);
       HttpHost proxy = new HttpHost(KieConfig.INSTANCE.getProxyHost(),
-          KieConfig.INSTANCE.getProxyPort(),"http"); // now only support http proxy
+          KieConfig.INSTANCE.getProxyPort(), "http"); // now only support http proxy
       httpClientBuilder.setProxy(proxy);
       CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
       credentialsProvider.setCredentials(new AuthScope(proxy),
@@ -163,7 +163,7 @@
 
   private KieAddressManager configKieAddressManager() {
     KieAddressManager kieAddressManager = new KieAddressManager(
-        Arrays.asList(KieConfig.INSTANCE.getServerUri().split(",")));
+        Arrays.asList(KieConfig.INSTANCE.getServerUri().split(",")), EventManager.getEventBus());
     return kieAddressManager;
   }
 
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstance.java
index 2c23982..47fbd96 100644
--- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstance.java
+++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstance.java
@@ -213,17 +213,14 @@
 
   private static void loadDataCenterInfo(MicroserviceInstance microserviceInstance) {
     String dataCenterName = DynamicPropertyFactory.getInstance()
-        .getStringProperty("servicecomb.datacenter.name", null)
+        .getStringProperty("servicecomb.datacenter.name", "default")
         .get();
-    if (StringUtils.isNotEmpty(dataCenterName)) {
-      DataCenterInfo dataCenterInfo = new DataCenterInfo();
-      dataCenterInfo.setName(dataCenterName);
-      dataCenterInfo
-          .setRegion(
-              DynamicPropertyFactory.getInstance().getStringProperty("servicecomb.datacenter.region", null).get());
-      dataCenterInfo.setAvailableZone(
-          DynamicPropertyFactory.getInstance().getStringProperty("servicecomb.datacenter.availableZone", null).get());
-      microserviceInstance.setDataCenterInfo(dataCenterInfo);
-    }
+    DataCenterInfo dataCenterInfo = new DataCenterInfo();
+    dataCenterInfo.setName(dataCenterName);
+    dataCenterInfo.setRegion(DynamicPropertyFactory.getInstance().
+        getStringProperty("servicecomb.datacenter.region", "default").get());
+    dataCenterInfo.setAvailableZone(DynamicPropertyFactory.getInstance().
+        getStringProperty("servicecomb.datacenter.availableZone", "default").get());
+    microserviceInstance.setDataCenterInfo(dataCenterInfo);
   }
 }
diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/AddressManager.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/AddressManager.java
index 99eadcf..d2e3eea 100644
--- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/AddressManager.java
+++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/AddressManager.java
@@ -18,67 +18,24 @@
 package org.apache.servicecomb.huaweicloud.dashboard.monitor;
 
 
-import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.servicecomb.deployment.Deployment;
-import org.apache.servicecomb.deployment.SystemBootstrapInfo;
-import org.apache.servicecomb.foundation.common.event.EventManager;
-import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant;
-import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
-import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import org.apache.servicecomb.http.client.common.AbstractAddressManager;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
 
 
-public class AddressManager {
-  private static final String MONITOR_SERVICE_NAME = "CseMonitoring";
+public class AddressManager extends AbstractAddressManager {
 
-  private static final String MONITOR_APPLICATION = "default";
-
-  private static final String MONITOR_VERSION = "latest";
-
-  private final List<String> addresses = new ArrayList<>();
-
-  private int index = 0;
-
-  AddressManager() {
-    updateAddresses();
-    updateServersFromSC();
-    EventManager.register(this);
+  AddressManager(List<String> addresses, EventBus eventBus) {
+    super(addresses);
+    eventBus.register(this);
   }
 
-  private void updateAddresses() {
-    SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(
-        MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE);
-    if (info != null && info.getAccessURL() != null) {
-      addresses.addAll(info.getAccessURL());
-    }
-  }
-
-  String nextServer() {
-    if (addresses.size() == 0) {
-      return null;
-    }
-    synchronized (this) {
-      this.index++;
-      if (this.index >= addresses.size()) {
-        this.index = 0;
-      }
-      return addresses.get(index);
-    }
-  }
-
-  private void updateServersFromSC() {
-    List<MicroserviceInstance> servers = RegistryUtils.findServiceInstance(MONITOR_APPLICATION,
-        MONITOR_SERVICE_NAME,
-        MONITOR_VERSION);
-    if (servers != null) {
-      for (MicroserviceInstance server : servers) {
-        for (String endpoint : server.getEndpoints()) {
-          if (!addresses.contains(endpoint)) {
-            addresses.add(endpoint);
-          }
-        }
-      }
-    }
+  @Subscribe
+  public void onRefreshEndpointEvent(RefreshEndpointEvent event) {
+    refreshEndpoint(event, RefreshEndpointEvent.CSE_MONITORING_NAME);
   }
 }
diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java
index 18ff61a..e3b5308 100644
--- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java
+++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java
@@ -17,9 +17,13 @@
 
 package org.apache.servicecomb.huaweicloud.dashboard.monitor;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.servicecomb.deployment.Deployment;
+import org.apache.servicecomb.deployment.SystemBootstrapInfo;
 import org.apache.servicecomb.foundation.auth.SignRequest;
 import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.common.net.IpPort;
@@ -67,7 +71,14 @@
   @Override
   public void init() {
     try {
-      addressManager = new AddressManager();
+      List<String> addresses = new ArrayList<>();
+      SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(
+          MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE);
+      if (info != null && info.getAccessURL() != null) {
+        addresses.addAll(info.getAccessURL());
+      }
+
+      addressManager = new AddressManager(addresses, EventManager.getEventBus());
       deployMonitorClient();
     } catch (Exception e) {
       LOGGER.warn("Deploy monitor data publisher failed will not send monitor data.");
@@ -80,7 +91,7 @@
     if (data == null) {
       return;
     }
-    String endpoint = addressManager.nextServer();
+    String endpoint = addressManager.address();
     if (endpoint == null) {
       return;
     }
@@ -110,6 +121,7 @@
         }
         return request.send(jsonData).compose(rsp -> {
           if (rsp.statusCode() != HttpResponseStatus.OK.code()) {
+            addressManager.recordSuccessState(endpoint);
             if (times < MonitorConstant.MAX_RETRY_TIMES
                 && rsp.statusCode() == HttpResponseStatus.BAD_GATEWAY.code()) {
               doSend(endpoint, jsonData, url, host, times + 1);
@@ -123,11 +135,13 @@
               return Future.succeededFuture();
             });
           } else {
+            addressManager.recordSuccessState(endpoint);
             EventManager.post(new MonitorSuccEvent());
           }
           return Future.succeededFuture();
         }).onFailure(failure -> {
           EventManager.post(new MonitorFailEvent("send monitor data fail."));
+          addressManager.recordFailState(endpoint);
           LOGGER.warn("Send monitor data to {} failed , {}", endpoint, failure);
         });
       });
diff --git a/huawei-cloud/dashboard/src/test/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/AddressManagerTest.java b/huawei-cloud/dashboard/src/test/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/AddressManagerTest.java
new file mode 100644
index 0000000..79cbb6b
--- /dev/null
+++ b/huawei-cloud/dashboard/src/test/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/AddressManagerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.huaweicloud.dashboard.monitor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.eventbus.EventBus;
+
+import mockit.Deencapsulation;
+
+class AddressManagerTest {
+
+  private static List<String> addresses = new ArrayList<>();
+
+  private static AddressManager addressManager1;
+
+  @Test
+  public void kieAddressManagerTest() {
+    addresses.add("http://127.0.0.1:30103");
+    addresses.add("https://127.0.0.2:30103");
+    addressManager1 = new AddressManager(addresses, new EventBus());
+
+    Assert.assertNotNull(addressManager1);
+
+    List<String> addresses = Deencapsulation.getField(addressManager1, "addresses");
+    Assert.assertEquals(2, addresses.size());
+    Assert.assertEquals("http://127.0.0.1:30103", addresses.get(0));
+
+    Assert.assertEquals("https://127.0.0.2:30103", addressManager1.address());
+    Assert.assertEquals("http://127.0.0.1:30103", addressManager1.address());
+  }
+
+
+  @Test
+  public void onRefreshEndpointEvent() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("http://127.0.0.3:30100");
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("http://127.0.0.4:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", addressRG);
+    addressManager1 = new AddressManager(addresses, new EventBus());
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseMonitoring");
+    addressManager1.refreshEndpoint(event, "CseMonitoring");
+
+    List<String> availableZone = Deencapsulation.getField(addressManager1, "availableZone");
+    Assert.assertEquals("http://127.0.0.3:30100", availableZone.get(0));
+
+    List<String> availableRegion = Deencapsulation.getField(addressManager1, "availableRegion");
+    Assert.assertEquals("http://127.0.0.4:30100", availableRegion.get(0));
+  }
+}
\ No newline at end of file
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java
index d2ff215..125d77a 100644
--- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java
@@ -185,6 +185,12 @@
 
   public static final String REGISTRY_SERVICE_NAME = "SERVICECENTER";
 
+  public static final String KIE_NAME = "KIE";
+
+  public static final String CONFIG_CENTER_NAME = "CseConfigCenter";
+
+  public static final String CSE_MONITORING_NAME = "CseMonitoring";
+
   public static final String PATH_CHECKSESSION = "checksession";
 
   public static final int SERVICE_CENTER_ORDER = 100;
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Type.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Type.java
new file mode 100644
index 0000000..d198f9f
--- /dev/null
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Type.java
@@ -0,0 +1,25 @@
+/*
+ * 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.api;
+
+public enum Type {
+  SERVICECENTER,
+  KIE,
+  CseConfigCenter,
+  CseMonitoring
+}
\ No newline at end of file
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/RBACBootStrapService.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/RBACBootStrapService.java
index c7a4914..80250ed 100644
--- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/RBACBootStrapService.java
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/RBACBootStrapService.java
@@ -27,6 +27,7 @@
 import org.apache.servicecomb.foundation.auth.Cipher;
 import org.apache.servicecomb.foundation.auth.DefaultCipher;
 import org.apache.servicecomb.foundation.bootstrap.BootStrapService;
+import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 import org.apache.servicecomb.foundation.ssl.SSLCustom;
 import org.apache.servicecomb.foundation.ssl.SSLOption;
@@ -91,7 +92,7 @@
 
   private AddressManager createAddressManager(Environment environment) {
     return new AddressManager(getTenantName(environment),
-        getRBACAddressList(environment));
+        getRBACAddressList(environment), EventManager.getEventBus());
   }
 
   private SSLProperties createSSLProperties(Environment environment, String tag) {
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/IpPortManager.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/IpPortManager.java
index e9728bf..58aec33 100644
--- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/IpPortManager.java
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/IpPortManager.java
@@ -17,23 +17,19 @@
 
 package org.apache.servicecomb.serviceregistry.client;
 
-import static org.apache.servicecomb.serviceregistry.api.Const.REGISTRY_APP_ID;
-import static org.apache.servicecomb.serviceregistry.api.Const.REGISTRY_SERVICE_NAME;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
+import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.common.net.IpPort;
-import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
-import org.apache.servicecomb.registry.cache.CacheEndpoint;
-import org.apache.servicecomb.registry.cache.InstanceCache;
 import org.apache.servicecomb.registry.cache.InstanceCacheManager;
 import org.apache.servicecomb.registry.cache.InstanceCacheManagerNew;
 import org.apache.servicecomb.registry.consumer.AppManager;
-import org.apache.servicecomb.registry.definition.DefinitionConst;
+import org.apache.servicecomb.serviceregistry.api.Type;
 import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
+import org.apache.servicecomb.serviceregistry.refresh.AddressManager;
+import org.apache.servicecomb.serviceregistry.refresh.ClassificationAddress;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,78 +40,52 @@
 
   InstanceCacheManager instanceCacheManager;
 
-  private String defaultTransport = "rest";
-
   private ArrayList<IpPort> defaultIpPort;
 
-  private AtomicInteger currentAvailableIndex;
-
   private boolean autoDiscoveryInited = false;
 
-  private int maxRetryTimes;
+  private AddressManager addressManger;
+
+  ClassificationAddress classificationAddress;
+
+  private Object lock = new Object();
 
   public void setAutoDiscoveryInited(boolean autoDiscoveryInited) {
     this.autoDiscoveryInited = autoDiscoveryInited;
   }
 
   public int getMaxRetryTimes() {
-    return maxRetryTimes;
+    return classificationAddress.getMaxRetryTimes();
   }
 
   public IpPortManager(ServiceRegistryConfig serviceRegistryConfig) {
     this.serviceRegistryConfig = serviceRegistryConfig;
     this.instanceCacheManager = new InstanceCacheManagerNew(new AppManager());
-
-    defaultTransport = serviceRegistryConfig.getTransport();
     defaultIpPort = serviceRegistryConfig.getIpPort();
     if (defaultIpPort.isEmpty()) {
       throw new IllegalArgumentException("Service center address is required to start the application.");
     }
-    int initialIndex = new Random().nextInt(defaultIpPort.size());
-    currentAvailableIndex = new AtomicInteger(initialIndex);
+    List<String> addresses = defaultIpPort.stream().map(IpPort::toString).collect(Collectors.toList());
+    addressManger = new AddressManager(addresses, EventManager.getEventBus());
+    classificationAddress = new ClassificationAddress(serviceRegistryConfig, instanceCacheManager);
     LOGGER.info("Initial service center address is {}", getAvailableAddress());
-    maxRetryTimes = defaultIpPort.size();
   }
 
   // we have to do this operation after the first time setup has already done
   public void initAutoDiscovery() {
     if (!autoDiscoveryInited && this.serviceRegistryConfig.isRegistryAutoDiscovery()) {
-      InstanceCache cache = instanceCacheManager.getOrCreate(REGISTRY_APP_ID,
-          REGISTRY_SERVICE_NAME,
-          DefinitionConst.VERSION_RULE_LATEST);
-      if (cache.getInstanceMap().size() > 0) {
-        setAutoDiscoveryInited(true);
-      } else {
-        setAutoDiscoveryInited(false);
+      for (Type type : Type.values()) {
+        classificationAddress.initEndPoint(type.name());
       }
+      setAutoDiscoveryInited(true);
     }
   }
 
   public IpPort getAvailableAddress() {
-    return getAvailableAddress(currentAvailableIndex.incrementAndGet());
+    return addressManger.getAvailableIpPort();
   }
 
-  private IpPort getAvailableAddress(int index) {
-    if (index < defaultIpPort.size()) {
-      return defaultIpPort.get(index);
-    }
-    List<CacheEndpoint> endpoints = getDiscoveredIpPort();
-    if (endpoints == null || (index >= defaultIpPort.size() + endpoints.size())) {
-      currentAvailableIndex.set(0);
-      return defaultIpPort.get(0);
-    }
-    maxRetryTimes = defaultIpPort.size() + endpoints.size();
-    CacheEndpoint nextEndpoint = endpoints.get(index - defaultIpPort.size());
-    return new URIEndpointObject(nextEndpoint.getEndpoint());
-  }
-
-  private List<CacheEndpoint> getDiscoveredIpPort() {
-    if (!autoDiscoveryInited || !this.serviceRegistryConfig.isRegistryAutoDiscovery()) {
-      return null;
-    }
-    InstanceCache instanceCache = instanceCacheManager.getOrCreate(REGISTRY_APP_ID,
-        REGISTRY_SERVICE_NAME,
-        DefinitionConst.VERSION_RULE_LATEST);
-    return instanceCache.getOrCreateTransportMap().get(defaultTransport);
+  public void recordState(String address) {
+    addressManger.recordFailState(address);
   }
 }
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java
index 2735ab3..c94af37 100644
--- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java
@@ -123,6 +123,7 @@
 
   private void retry(RequestContext requestContext, Handler<RestResponse> responseHandler) {
     String oldUri = requestContext.getIpPort().toString();
+    ipPortManager.recordState(oldUri);
     requestContext.setIpPort(ipPortManager.getAvailableAddress());
     String newUri = requestContext.getIpPort().toString();
     LOGGER.warn("invoke service [{}] failed, retry address [{}].", oldUri, newUri);
@@ -290,6 +291,7 @@
                 }
                 break;
                 default:
+                  ipPortManager.recordState(requestContext.getIpPort().toString());
                   LOGGER.warn("failed to findInstances: " + bodyBuffer.toString());
                   break;
               }
@@ -672,6 +674,7 @@
           response.setOk(true);
           return response;
         }
+        ipPortManager.recordState(ipPort.toString());
         LOGGER.warn(holder.value.statusMessage());
         return response;
       }
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/refresh/AddressManager.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/refresh/AddressManager.java
new file mode 100644
index 0000000..fd1ce16
--- /dev/null
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/refresh/AddressManager.java
@@ -0,0 +1,58 @@
+/*
+ * 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.refresh;
+
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.apache.servicecomb.http.client.common.AbstractAddressManager;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
+public class AddressManager extends AbstractAddressManager {
+
+  private static final String URI_SPLIT = ":";
+
+  public AddressManager(List<String> addresses, EventBus eventBus) {
+    super(addresses);
+    eventBus.register(this);
+  }
+
+  public IpPort getAvailableIpPort() {
+    return transformIpPort(this.address());
+  }
+
+  @Override
+  protected String normalizeUri(String endpoint) {
+    return new URIEndpointObject(endpoint).toString();
+  }
+
+  private IpPort transformIpPort(String address) {
+    String[] result = StringUtils.split(address, URI_SPLIT);
+    return new IpPort(result[0], Integer.valueOf(result[1]));
+  }
+
+  @Subscribe
+  public void onRefreshEndpointEvent(RefreshEndpointEvent event) {
+    refreshEndpoint(event, RefreshEndpointEvent.SERVICE_CENTER_NAME);
+  }
+}
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/refresh/ClassificationAddress.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/refresh/ClassificationAddress.java
new file mode 100644
index 0000000..2c2ccda
--- /dev/null
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/refresh/ClassificationAddress.java
@@ -0,0 +1,191 @@
+/*
+ * 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.refresh;
+
+import static org.apache.servicecomb.serviceregistry.api.Const.CONFIG_CENTER_NAME;
+import static org.apache.servicecomb.serviceregistry.api.Const.CSE_MONITORING_NAME;
+import static org.apache.servicecomb.serviceregistry.api.Const.KIE_NAME;
+import static org.apache.servicecomb.serviceregistry.api.Const.REGISTRY_APP_ID;
+import static org.apache.servicecomb.serviceregistry.api.Const.REGISTRY_SERVICE_NAME;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.foundation.common.event.EventManager;
+import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.apache.servicecomb.registry.RegistrationManager;
+import org.apache.servicecomb.registry.api.registry.DataCenterInfo;
+import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
+import org.apache.servicecomb.registry.cache.CacheEndpoint;
+import org.apache.servicecomb.registry.cache.InstanceCache;
+import org.apache.servicecomb.registry.cache.InstanceCacheManager;
+import org.apache.servicecomb.registry.definition.DefinitionConst;
+import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
+import org.apache.servicecomb.serviceregistry.event.ServiceCenterEventBus;
+import org.apache.servicecomb.serviceregistry.registry.cache.MicroserviceCache;
+import org.apache.servicecomb.serviceregistry.registry.cache.MicroserviceCacheRefreshedEvent;
+
+import com.google.common.eventbus.Subscribe;
+
+public class ClassificationAddress {
+
+  private static final String SC_KEY = "SERVICECENTER@default@@0.0.0.0+";
+
+  private static final String CC_KEY = "CseConfigCenter@default@@0.0.0.0+";
+
+  private static final String KIE_KEY = "KIE@default@@0.0.0.0+";
+
+  private static final String MONITORING_KEY = "CseMonitoring@default@@0.0.0.0+";
+
+  private String defaultTransport = "rest";
+
+  private DataCenterInfo dataCenterInfo;
+
+  InstanceCacheManager instanceCacheManager;
+
+  private ArrayList<IpPort> defaultIpPort;
+
+  private int maxRetryTimes;
+
+  public ClassificationAddress(ServiceRegistryConfig serviceRegistryConfig, InstanceCacheManager instanceCacheManager) {
+    this.defaultTransport = serviceRegistryConfig.getTransport();
+    this.defaultIpPort = serviceRegistryConfig.getIpPort();
+    this.instanceCacheManager = instanceCacheManager;
+    this.maxRetryTimes = defaultIpPort.size();
+    ServiceCenterEventBus.getEventBus().register(this);
+  }
+
+  public void initEndPoint(String typeName) {
+    Map<String, List<String>> zoneAndRegion = generateZoneAndRegionAddress(typeName);
+    if (zoneAndRegion == null) {
+      return;
+    }
+    EventManager.post(new RefreshEndpointEvent(zoneAndRegion, typeName));
+  }
+
+  @Subscribe
+  public void onMicroserviceCacheRefreshed(MicroserviceCacheRefreshedEvent event) {
+    List<MicroserviceCache> microserviceCaches = event.getMicroserviceCaches();
+    if (null == microserviceCaches || microserviceCaches.isEmpty()) {
+      return;
+    }
+
+    for (MicroserviceCache microserviceCache : microserviceCaches) {
+      if (microserviceCache.getKey().toString().equals(SC_KEY)) {
+        refreshEndPoints(microserviceCache, REGISTRY_SERVICE_NAME);
+      }
+      if (microserviceCache.getKey().toString().equals(CC_KEY)) {
+        refreshEndPoints(microserviceCache, KIE_NAME);
+      }
+      if (microserviceCache.getKey().toString().equals(KIE_KEY)) {
+        refreshEndPoints(microserviceCache, CONFIG_CENTER_NAME);
+      }
+      if (microserviceCache.getKey().toString().equals(MONITORING_KEY)) {
+        refreshEndPoints(microserviceCache, CSE_MONITORING_NAME);
+      }
+    }
+  }
+
+  private void refreshEndPoints(MicroserviceCache microserviceCache, String name) {
+    Map<String, List<String>> zoneAndRegion = refreshEndPoint(microserviceCache);
+    EventManager.post(new RefreshEndpointEvent(zoneAndRegion, name));
+  }
+
+  private Map<String, List<String>> refreshEndPoint(MicroserviceCache microserviceCache) {
+    List<String> sameZone = new ArrayList<>();
+    List<String> sameRegion = new ArrayList<>();
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+
+    List<MicroserviceInstance> microserviceCacheInstances = microserviceCache.getInstances();
+
+    microserviceCacheInstances.forEach(microserviceInstance -> {
+      String endPoint = microserviceInstance.getEndpoints().get(0);
+      if (regionAndAZMatch(dataCenterInfo, microserviceInstance)) {
+        sameZone.add(endPoint);
+      } else {
+        sameRegion.add(endPoint);
+      }
+    });
+    zoneAndRegion.put("sameZone", sameZone);
+    zoneAndRegion.put("sameRegion", sameRegion);
+    return zoneAndRegion;
+  }
+
+  private Map<String, List<String>> generateZoneAndRegionAddress(String key) {
+    InstanceCache KieCaches = instanceCacheManager
+        .getOrCreate(REGISTRY_APP_ID, key, DefinitionConst.VERSION_RULE_LATEST);
+    List<CacheEndpoint> CacheEndpoints = new ArrayList<>();
+    if (REGISTRY_SERVICE_NAME.equals(key)) {
+      CacheEndpoints = KieCaches.getOrCreateTransportMap().get(defaultTransport);
+      maxRetryTimes = CacheEndpoints.size();
+    } else {
+      if (KieCaches.getInstanceMap().size() <= 0) {
+        return null;
+      }
+      CacheEndpoints = KieCaches.getOrCreateTransportMap().get(defaultTransport);
+    }
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    dataCenterInfo = findRegion(CacheEndpoints);
+
+    List<String> sameZone = new ArrayList<>();
+    List<String> sameRegion = new ArrayList<>();
+    for (CacheEndpoint cacheEndpoint : CacheEndpoints) {
+      if (regionAndAZMatch(dataCenterInfo, cacheEndpoint.getInstance())) {
+        sameZone.add(cacheEndpoint.getEndpoint());
+      } else {
+        sameRegion.add(cacheEndpoint.getEndpoint());
+      }
+    }
+    zoneAndRegion.put("sameZone", sameZone);
+    zoneAndRegion.put("sameRegion", sameRegion);
+    return zoneAndRegion;
+  }
+
+  private DataCenterInfo findRegion(List<CacheEndpoint> CacheEndpoints) {
+    MicroserviceInstance myself = RegistrationManager.INSTANCE.getMicroserviceInstance();
+    if (myself.getDataCenterInfo() == null) {
+      return null;
+    }
+    for (CacheEndpoint cacheEndpoint : CacheEndpoints) {
+      boolean isMatch = cacheEndpoint.getEndpoint().contains(this.defaultIpPort.get(0).getHostOrIp());
+      if (isMatch && cacheEndpoint.getInstance().getDataCenterInfo() != null) {
+        return cacheEndpoint.getInstance().getDataCenterInfo();
+      }
+    }
+    return null;
+  }
+
+  private boolean regionAndAZMatch(DataCenterInfo myself, MicroserviceInstance target) {
+    if (myself == null) {
+      // when instance have no datacenter info, it will match all other datacenters
+      return true;
+    }
+    if (target.getDataCenterInfo() != null) {
+      return myself.getRegion().equals(target.getDataCenterInfo().getRegion()) &&
+          myself.getAvailableZone().equals(target.getDataCenterInfo().getAvailableZone());
+    }
+    return false;
+  }
+
+  public int getMaxRetryTimes() {
+    return maxRetryTimes;
+  }
+}
diff --git a/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/TestIpPortManager.java b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/TestIpPortManager.java
index 11c9b85..68ca58f 100644
--- a/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/TestIpPortManager.java
+++ b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/TestIpPortManager.java
@@ -24,12 +24,14 @@
 
 import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.registry.RegistrationManager;
 import org.apache.servicecomb.serviceregistry.RegistryUtils;
 import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
 import org.apache.servicecomb.registry.cache.CacheEndpoint;
 import org.apache.servicecomb.registry.cache.InstanceCache;
 import org.apache.servicecomb.registry.cache.InstanceCacheManager;
 import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
+import org.apache.servicecomb.serviceregistry.refresh.ClassificationAddress;
 import org.apache.servicecomb.serviceregistry.registry.AbstractServiceRegistry;
 import org.apache.servicecomb.serviceregistry.registry.LocalServiceRegistryFactory;
 import org.junit.Assert;
@@ -59,7 +61,8 @@
   @Test
   public void testGetAvailableAddress(@Injectable ServiceRegistryConfig config,
       @Injectable InstanceCacheManager cacheManager,
-      @Injectable InstanceCache cache) {
+      @Injectable InstanceCache cache,
+      @Injectable ClassificationAddress classificationAddress) {
     ArrayList<IpPort> ipPortList = new ArrayList<>();
     ipPortList.add(new IpPort("127.0.0.1", 9980));
     ipPortList.add(new IpPort("127.0.0.1", 9981));
@@ -102,6 +105,8 @@
     List<CacheEndpoint> instances = new ArrayList<>();
     instances.add(new CacheEndpoint("http://127.0.0.1:9982", null));
     addresses.put("rest", instances);
+    ClassificationAddress classificationAddres = new ClassificationAddress(config, cacheManager);
+    manager.classificationAddress =classificationAddres;
     new Expectations() {
       {
         cacheManager.getOrCreate("default", "SERVICECENTER", "latest");
@@ -112,7 +117,6 @@
     };
 
     // test getAvailableAddress() when auto discovery is disabled
-    manager.initAutoDiscovery();  //init result is false at first time
     IpPort address4 = manager.getAvailableAddress();
     Assert.assertEquals("127.0.0.1", address4.getHostOrIp());
     if (address1.getPort() == 9980) {
@@ -120,12 +124,25 @@
     }
     Assert.assertEquals(9980, address4.getPort());
 
-    // test getAvailable address when auto discovery is enabled
-    manager.setAutoDiscoveryInited(true);
     IpPort address5 = manager.getAvailableAddress();
     Assert.assertEquals("127.0.0.1", address5.getHostOrIp());
     Assert.assertEquals(9981, address5.getPort());
 
+    //mock RegistrationManager.INSTANCE
+    String instanceId = "e8a04b54cf2711e7b701286ed488fc20";
+    MicroserviceInstance microserviceInstance = new MicroserviceInstance();
+    microserviceInstance.setInstanceId(instanceId);
+    Map<String, String> properties = new HashMap<>();
+    microserviceInstance.setProperties(properties);
+    new Expectations(RegistrationManager.INSTANCE) {
+      {
+        RegistrationManager.INSTANCE.getMicroserviceInstance();
+        result = microserviceInstance;
+      }
+    };
+    // test getAvailable address when auto discovery is enabled
+    manager.initAutoDiscovery();
+    manager.setAutoDiscoveryInited(true);
     IpPort address6 = manager.getAvailableAddress();
     Assert.assertEquals("127.0.0.1", address6.getHostOrIp());
     Assert.assertEquals(9982, address6.getPort());
diff --git a/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/refresh/AddressManagerTest.java b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/refresh/AddressManagerTest.java
new file mode 100644
index 0000000..a81a56c
--- /dev/null
+++ b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/refresh/AddressManagerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.refresh;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.eventbus.EventBus;
+
+import mockit.Deencapsulation;
+
+class AddressManagerTest {
+
+  private static List<String> addresses = new ArrayList<>();
+
+  private static AddressManager addressManager1;
+
+  private static AddressManager addressManager2;
+
+  @Test
+  public void addressManagerTest() {
+    IpPort ipPort = new IpPort("127.0.0.1", 30103);
+    addresses.add(ipPort.toString());
+    addressManager1 = new AddressManager(addresses, new EventBus());
+    addressManager2 = new AddressManager(addresses, new EventBus());
+
+    Assert.assertNotNull(addressManager1);
+    Assert.assertNotNull(addressManager2);
+
+    List<String> addresses = Deencapsulation.getField(addressManager1, "addresses");
+    Assert.assertEquals(1, addresses.size());
+    Assert.assertEquals("127.0.0.1:30103", addresses.get(0));
+    Assert.assertEquals("127.0.0.1:30103", addressManager1.address());
+
+    ipPort = addressManager2.getAvailableIpPort();
+    Assert.assertEquals("127.0.0.1:30103", ipPort.toString());
+    Assert.assertEquals("127.0.0.1", ipPort.getHostOrIp());
+    Assert.assertEquals(30103, ipPort.getPort());
+  }
+
+  @Test
+  public void onRefreshEndpointEvent() {
+    List<String> addressAZ = new ArrayList<>();
+    addressAZ.add("http://127.0.0.3:30100");
+    List<String> addressRG = new ArrayList<>();
+    addressRG.add("https://127.0.0.4:30100");
+    Map<String, List<String>> zoneAndRegion = new HashMap<>();
+    zoneAndRegion.put("sameZone", addressAZ);
+    zoneAndRegion.put("sameRegion", addressRG);
+    addressManager1 = new AddressManager(addresses, new EventBus());
+    RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "SERVICECENTER");
+    addressManager1.refreshEndpoint(event, "SERVICECENTER");
+
+    List<String> availableZone = Deencapsulation.getField(addressManager1, "availableZone");
+    Assert.assertEquals("127.0.0.3:30100", availableZone.get(0));
+
+    List<String> availableRegion = Deencapsulation.getField(addressManager1, "availableRegion");
+    Assert.assertEquals("127.0.0.4:30100", availableRegion.get(0));
+  }
+}
\ No newline at end of file