You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2020/04/01 12:35:16 UTC

[servicecomb-java-chassis] branch master updated: [SCB-1843]refator: provide a way to initialze all vert.x http client easie

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

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new aa8fd05  [SCB-1843]refator: provide a way to initialze all vert.x http client easie
aa8fd05 is described below

commit aa8fd05ae77bad57e0aaa372dfcebd2043b00891
Author: liubao <bi...@qq.com>
AuthorDate: Wed Apr 1 14:53:23 2020 +0800

    [SCB-1843]refator: provide a way to initialze all vert.x http client easie
---
 .../servicecomb/core/CseApplicationListener.java   |   2 +
 .../org/apache/servicecomb/core/SCBEngine.java     |  13 +-
 .../vertx/client/http/HttpClientOptionsSPI.java    | 101 +++++++++
 .../foundation/vertx/client/http/HttpClients.java  |  97 +++++++++
 .../metrics/core/TestVertxMetersInitializer.java   |   7 +-
 .../client/http/AbstractClientPool.java            |  66 +-----
 .../serviceregistry/client/http/ClientPool.java    |   2 -
 .../client/http/HttpClientPool.java                |  57 +-----
 .../client/http/RegistryHttpClientOptionsSPI.java  | 123 +++++++++++
 .../http/RegistryWatchHttpClientOptionsSPI.java    |  66 ++++++
 .../client/http/RestClientUtil.java                |   2 +-
 .../serviceregistry/client/http/RestUtils.java     | 226 ---------------------
 .../client/http/WebsocketClientPool.java           |  40 +---
 .../client/http/WebsocketClientUtil.java           |   2 +-
 .../client/http/WebsocketUtils.java                |  76 -------
 ...undation.vertx.client.http.HttpClientOptionsSPI |  19 ++
 .../serviceregistry/client/http/RestUtilsTest.java |  47 -----
 .../client/http/TestAbstractClientPool.java        |  16 --
 .../client/http/TestHttpClientPool.java            |  74 -------
 .../client/http/TestServiceRegistryClientImpl.java |   4 +
 .../client/http/TestWebsocketClientPool.java       |  34 ----
 21 files changed, 434 insertions(+), 640 deletions(-)

diff --git a/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java b/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
index a8ffdd0..a3153ff 100644
--- a/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
+++ b/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
@@ -18,6 +18,7 @@
 package org.apache.servicecomb.core;
 
 import org.apache.servicecomb.foundation.common.utils.BeanUtils;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
 import org.apache.servicecomb.serviceregistry.RegistryUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
@@ -39,6 +40,7 @@ public class CseApplicationListener
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
     this.applicationContext = applicationContext;
     BeanUtils.setContext(applicationContext);
+    HttpClients.load();
     RegistryUtils.init();
     SCBEngine.getInstance();
   }
diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
index 88dc41e..04234c2 100644
--- a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
+++ b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
@@ -53,6 +53,7 @@ import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation
 import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 import org.apache.servicecomb.foundation.vertx.VertxUtils;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
 import org.apache.servicecomb.serviceregistry.RegistryUtils;
 import org.apache.servicecomb.serviceregistry.ServiceRegistry;
 import org.apache.servicecomb.serviceregistry.api.registry.MicroserviceInstance;
@@ -405,7 +406,6 @@ public class SCBEngine {
     //Step 3: Unregister microservice instance from Service Center and close vertx
     // Forbidden other consumers find me
     RegistryUtils.destroy();
-    VertxUtils.blockCloseVertxByName("registry");
     serviceRegistryListener.destroy();
 
     //Step 4: wait all invocation finished
@@ -415,13 +415,16 @@ public class SCBEngine {
       LOGGER.error("wait all invocation finished interrupted", e);
     }
 
-    //Step 5: Stop vertx to prevent blocking exit
+    //Step 5: destroy config center source
+    ConfigUtil.destroyConfigCenterConfigurationSource();
+    priorityPropertyManager.close();
+
+    //Step 6: Stop vertx to prevent blocking exit
+	// delete the following two lines when every refactor is done.
     VertxUtils.blockCloseVertxByName("config-center");
     VertxUtils.blockCloseVertxByName("transport");
 
-    //Step 6: destroy config center source
-    ConfigUtil.destroyConfigCenterConfigurationSource();
-    priorityPropertyManager.close();
+    HttpClients.destroy();
 
     //Step 7: notify all component do clean works via AFTER_CLOSE Event
     safeTriggerEvent(EventType.AFTER_CLOSE);
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java
new file mode 100644
index 0000000..a538a42
--- /dev/null
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java
@@ -0,0 +1,101 @@
+/*
+ * 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.foundation.vertx.client.http;
+
+import org.apache.servicecomb.foundation.common.encrypt.Encryptions;
+import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder;
+
+import io.vertx.core.http.HttpClientOptions;
+import io.vertx.core.http.HttpVersion;
+import io.vertx.core.net.ProxyOptions;
+
+/**
+ * common Http Client Options must be set by implementations
+ */
+public interface HttpClientOptionsSPI {
+  /* unique name in this service */
+  String clientName();
+
+  /* loading order */
+  int getOrder();
+
+  /* can turn off the client */
+  boolean enabled();
+
+  /* config tag is used for group configurations, like ssl, address resolver, etc. set config tag to distinguish
+  *  other clients configuration or read the common configuration. */
+  String getConfigTag();
+
+  /*****************  vert.x common settings ***************************/
+  int getEventLoopPoolSize();
+
+  int getInstanceCount();
+
+  boolean isWorker();
+
+  String getWorkerPoolName();
+
+  int getWorkerPoolSize();
+
+  /*****************  http common settings ***************************/
+  HttpVersion getHttpVersion();
+
+  int getConnectTimeoutInMillis();
+
+  int getIdleTimeoutInSeconds();
+
+  /*****************  proxy settings ***************************/
+  boolean isProxyEnable();
+
+  String getProxyHost();
+
+  int getProxyPort();
+
+  String getProxyUsername();
+
+  String getProxyPassword();
+
+  /*****************  ssl settings ***************************/
+  boolean isSsl();
+
+  static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) {
+    HttpClientOptions httpClientOptions = new HttpClientOptions();
+
+    httpClientOptions.setProtocolVersion(spi.getHttpVersion());
+    httpClientOptions.setConnectTimeout(spi.getConnectTimeoutInMillis());
+    httpClientOptions.setIdleTimeout(spi.getIdleTimeoutInSeconds());
+    if (spi.isProxyEnable()) {
+      ProxyOptions proxy = new ProxyOptions();
+      proxy.setHost(spi.getProxyHost());
+      proxy.setPort(spi.getProxyPort());
+      proxy.setUsername(spi.getProxyUsername());
+      proxy.setPassword(
+          Encryptions.decode(spi.getProxyPassword(), spi.getConfigTag()));
+      httpClientOptions.setProxyOptions(proxy);
+    }
+    if (spi.getHttpVersion() == HttpVersion.HTTP_2) {
+      httpClientOptions.setHttp2ClearTextUpgrade(false);
+    }
+
+    if (spi.isSsl()) {
+      VertxTLSBuilder.buildHttpClientOptions(spi.getConfigTag(), httpClientOptions);
+    }
+
+    return httpClientOptions;
+  }
+}
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java
new file mode 100644
index 0000000..2ddbd6b
--- /dev/null
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java
@@ -0,0 +1,97 @@
+/*
+ * 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.foundation.vertx.client.http;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
+import org.apache.servicecomb.foundation.vertx.AddressResolverConfig;
+import org.apache.servicecomb.foundation.vertx.VertxUtils;
+import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager;
+import org.apache.servicecomb.foundation.vertx.client.ClientVerticle;
+
+import io.vertx.core.DeploymentOptions;
+import io.vertx.core.Vertx;
+import io.vertx.core.VertxOptions;
+
+/**
+ *  load and manages a set of HttpClient at boot up.
+ */
+public class HttpClients {
+  private static Map<String, ClientPoolManager<HttpClientWithContext>> httpClients;
+
+  /* load at boot up, call this method once and only once. */
+  public static void load() {
+    httpClients = new HashMap<>();
+    List<HttpClientOptionsSPI> clientOptionsList = SPIServiceUtils.getOrLoadSortedService(HttpClientOptionsSPI.class);
+    clientOptionsList.forEach(option -> {
+      if (option.enabled()) {
+        ClientPoolManager<HttpClientWithContext> clientPoolManager = httpClients.get(option.clientName());
+        if (clientPoolManager != null) {
+          throw new IllegalStateException(
+              "Can not configure two http client with the same name: " + option.clientName());
+        }
+        httpClients.put(option.clientName(), createClientPoolManager(option));
+      }
+    });
+  }
+
+  /* destroy at shutdown. */
+  public static void destroy() {
+    httpClients = null;
+    List<HttpClientOptionsSPI> clientOptionsList = SPIServiceUtils.getOrLoadSortedService(HttpClientOptionsSPI.class);
+    clientOptionsList.forEach(option -> {
+      VertxUtils.blockCloseVertxByName(option.clientName());
+    });
+  }
+
+  private static ClientPoolManager<HttpClientWithContext> createClientPoolManager(HttpClientOptionsSPI option) {
+    VertxOptions vertxOptions = new VertxOptions()
+        .setAddressResolverOptions(AddressResolverConfig.getAddressResover(option.getConfigTag()))
+        .setEventLoopPoolSize(option.getEventLoopPoolSize());
+
+    // Maybe we can deploy only one vert.x for the application. However this has did it like this.
+    Vertx vertx = VertxUtils.getOrCreateVertxByName(option.clientName(), vertxOptions);
+
+    ClientPoolManager<HttpClientWithContext> clientPoolManager = new ClientPoolManager<>(vertx,
+        new HttpClientPoolFactory(HttpClientOptionsSPI.createHttpClientOptions(option)));
+
+    DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientPoolManager,
+        option.getInstanceCount())
+        .setWorker(option.isWorker())
+        .setWorkerPoolName(option.getWorkerPoolName())
+        .setWorkerPoolSize(option.getWorkerPoolSize());
+    try {
+      VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions);
+    } catch (InterruptedException e) {
+      throw new IllegalStateException(e);
+    }
+    return clientPoolManager;
+  }
+
+  /**
+   * get client instance by name
+   * @param clientName instance name
+   * @return the deployed instance name
+   */
+  public static HttpClientWithContext getClient(String clientName) {
+    return httpClients.get(clientName).findThreadBindClientPool();
+  }
+}
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java
index 4301313..02a0259 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java
@@ -26,6 +26,7 @@ import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
 import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
 import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector;
 import org.apache.servicecomb.foundation.vertx.VertxUtils;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
 import org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher;
 import org.junit.After;
 import org.junit.Assert;
@@ -104,12 +105,12 @@ public class TestVertxMetersInitializer {
 
   @Before
   public void setup() {
-    VertxUtils.blockCloseVertxByName("transport");
+    HttpClients.load();
   }
 
   @After
   public void teardown() {
-    VertxUtils.blockCloseVertxByName("transport");
+    HttpClients.destroy();
   }
 
   @Test
@@ -168,6 +169,8 @@ public class TestVertxMetersInitializer {
     String expect = "vertx:\n"
         + "  instances:\n"
         + "    name       eventLoopContext-created\n"
+        + "    registry   0\n"
+        + "    registry-watch 0\n"
         + "    transport  0\n"
         + "  transport:\n"
         + "    client.endpoints:\n"
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/AbstractClientPool.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/AbstractClientPool.java
index c709692..3882015 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/AbstractClientPool.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/AbstractClientPool.java
@@ -17,73 +17,13 @@
 
 package org.apache.servicecomb.serviceregistry.client.http;
 
-import org.apache.servicecomb.foundation.vertx.AddressResolverConfig;
-import org.apache.servicecomb.foundation.vertx.VertxUtils;
-import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager;
-import org.apache.servicecomb.foundation.vertx.client.ClientVerticle;
-import org.apache.servicecomb.foundation.vertx.client.http.HttpClientPoolFactory;
 import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
-import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
 
-import com.netflix.config.DynamicIntProperty;
-import com.netflix.config.DynamicPropertyFactory;
-
-import io.vertx.core.DeploymentOptions;
-import io.vertx.core.Vertx;
-import io.vertx.core.VertxOptions;
-import io.vertx.core.http.HttpClientOptions;
-
-/**
- * Created by  on 2017/4/28.
- */
 abstract class AbstractClientPool implements ClientPool {
-  private static final Logger LOGGER = LoggerFactory.getLogger(AbstractClientPool.class);
-
-  private ServiceRegistryConfig serviceRegistryConfig;
-
-  private HttpClientOptions httpClientOptions;
-
-  private ClientPoolManager<HttpClientWithContext> clientMgr;
-
-  AbstractClientPool(ServiceRegistryConfig serviceRegistryConfig) {
-    this.serviceRegistryConfig = serviceRegistryConfig;
-    this.httpClientOptions = getHttpClientOptionsFromConfigurations(serviceRegistryConfig);
-    create();
-  }
-
-  protected abstract boolean isWorker();
-
-  protected abstract HttpClientOptions getHttpClientOptionsFromConfigurations(
-      ServiceRegistryConfig serviceRegistryConfig);
+  public abstract String getName();
 
   public HttpClientWithContext getClient() {
-    return this.clientMgr.findThreadBindClientPool();
-  }
-
-  public void create() {
-    DynamicIntProperty property = DynamicPropertyFactory.getInstance()
-        .getIntProperty(ServiceRegistryConfig.EVENT_LOOP_POOL_SIZE, 4);
-    DynamicIntProperty workerPoolSize = DynamicPropertyFactory.getInstance()
-        .getIntProperty(ServiceRegistryConfig.WORKER_POOL_SIZE, 4);
-
-    // 这里面是同步接口,且好像直接在事件线程中用,保险起见,先使用独立的vertx实例
-    VertxOptions vertxOptions = new VertxOptions()
-        .setAddressResolverOptions(AddressResolverConfig.getAddressResover(serviceRegistryConfig.getSslConfigTag()))
-        .setEventLoopPoolSize(property.get());
-    Vertx vertx = VertxUtils.getOrCreateVertxByName("registry", vertxOptions);
-    clientMgr = new ClientPoolManager<>(vertx, new HttpClientPoolFactory(httpClientOptions));
-
-    DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(this.clientMgr,
-        ServiceRegistryConfig.INSTANCE.getInstances())
-        .setWorker(isWorker())
-        .setWorkerPoolName(ServiceRegistryConfig.WORKER_POOL_NAME)
-        .setWorkerPoolSize(workerPoolSize.get());
-    try {
-      VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions);
-    } catch (InterruptedException e) {
-      LOGGER.error("deploy a registry verticle failed, {}", e.getMessage());
-    }
+    return HttpClients.getClient(getName());
   }
 }
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ClientPool.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ClientPool.java
index 3784d98..24a4c04 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ClientPool.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ClientPool.java
@@ -23,7 +23,5 @@ import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext
  * Created by on 2017/4/28.
  */
 interface ClientPool {
-  void create();
-
   HttpClientWithContext getClient();
 }
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/HttpClientPool.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/HttpClientPool.java
index 962a81c..fd29788 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/HttpClientPool.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/HttpClientPool.java
@@ -17,62 +17,9 @@
 
 package org.apache.servicecomb.serviceregistry.client.http;
 
-import org.apache.servicecomb.foundation.common.encrypt.Encryptions;
-import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder;
-import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.vertx.core.http.HttpClientOptions;
-import io.vertx.core.http.HttpVersion;
-import io.vertx.core.net.ProxyOptions;
-
 class HttpClientPool extends AbstractClientPool {
-
-  private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientPool.class);
-
-  /**
-   * The default instance, for default sc cluster.
-   */
-  public static final HttpClientPool INSTANCE = new HttpClientPool();
-
-  private HttpClientPool() {
-    super(ServiceRegistryConfig.INSTANCE);
-  }
-
-  HttpClientPool(ServiceRegistryConfig serviceRegistryConfig) {
-    super(serviceRegistryConfig);
-  }
-
-  @Override
-  protected boolean isWorker() {
-    return false;
-  }
-
   @Override
-  protected HttpClientOptions getHttpClientOptionsFromConfigurations(ServiceRegistryConfig serviceRegistryConfig) {
-    HttpVersion ver = serviceRegistryConfig.getHttpVersion();
-    HttpClientOptions httpClientOptions = new HttpClientOptions();
-    httpClientOptions.setProtocolVersion(ver);
-    httpClientOptions.setConnectTimeout(serviceRegistryConfig.getConnectionTimeout());
-    httpClientOptions.setIdleTimeout(serviceRegistryConfig.getIdleConnectionTimeout());
-    if (serviceRegistryConfig.isProxyEnable()) {
-      ProxyOptions proxy = new ProxyOptions();
-      proxy.setHost(serviceRegistryConfig.getProxyHost());
-      proxy.setPort(serviceRegistryConfig.getProxyPort());
-      proxy.setUsername(serviceRegistryConfig.getProxyUsername());
-      proxy.setPassword(
-          Encryptions.decode(serviceRegistryConfig.getProxyPasswd(), serviceRegistryConfig.getProxyConfigTag()));
-      httpClientOptions.setProxyOptions(proxy);
-    }
-    if (ver == HttpVersion.HTTP_2) {
-      LOGGER.debug("service center client protocol version is HTTP/2");
-      httpClientOptions.setHttp2ClearTextUpgrade(false);
-    }
-    if (serviceRegistryConfig.isSsl()) {
-      LOGGER.debug("service center client performs requests over TLS");
-      VertxTLSBuilder.buildHttpClientOptions(serviceRegistryConfig.getSslConfigTag(), httpClientOptions);
-    }
-    return httpClientOptions;
+  public String getName() {
+    return RegistryHttpClientOptionsSPI.CLIENT_NAME;
   }
 }
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RegistryHttpClientOptionsSPI.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RegistryHttpClientOptionsSPI.java
new file mode 100644
index 0000000..304c10c
--- /dev/null
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RegistryHttpClientOptionsSPI.java
@@ -0,0 +1,123 @@
+/*
+ * 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.client.http;
+
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI;
+import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
+
+import com.netflix.config.DynamicPropertyFactory;
+
+import io.vertx.core.VertxOptions;
+import io.vertx.core.http.HttpVersion;
+
+public class RegistryHttpClientOptionsSPI implements HttpClientOptionsSPI {
+  public static final String CLIENT_NAME = "registry";
+
+  private ServiceRegistryConfig serviceRegistryConfig = ServiceRegistryConfig.INSTANCE;
+
+  @Override
+  public String clientName() {
+    return CLIENT_NAME;
+  }
+
+  @Override
+  public int getOrder() {
+    return -100;
+  }
+
+  @Override
+  public boolean enabled() {
+    return true;
+  }
+
+  @Override
+  public String getConfigTag() {
+    return "sc.consumer";
+  }
+
+  @Override
+  public int getEventLoopPoolSize() {
+    return DynamicPropertyFactory.getInstance()
+        .getIntProperty(ServiceRegistryConfig.EVENT_LOOP_POOL_SIZE, 4).get();
+  }
+
+  @Override
+  public int getInstanceCount() {
+    return serviceRegistryConfig.getInstances();
+  }
+
+  @Override
+  public boolean isWorker() {
+    return false;
+  }
+
+  @Override
+  public String getWorkerPoolName() {
+    return null;
+  }
+
+  @Override
+  public int getWorkerPoolSize() {
+    return VertxOptions.DEFAULT_WORKER_POOL_SIZE;
+  }
+
+  @Override
+  public HttpVersion getHttpVersion() {
+    return serviceRegistryConfig.getHttpVersion();
+  }
+
+  @Override
+  public int getConnectTimeoutInMillis() {
+    return serviceRegistryConfig.getConnectionTimeout();
+  }
+
+  @Override
+  public int getIdleTimeoutInSeconds() {
+    return serviceRegistryConfig.getIdleConnectionTimeout();
+  }
+
+  @Override
+  public boolean isProxyEnable() {
+    return serviceRegistryConfig.isProxyEnable();
+  }
+
+  @Override
+  public String getProxyHost() {
+    return serviceRegistryConfig.getProxyHost();
+  }
+
+  @Override
+  public int getProxyPort() {
+    return serviceRegistryConfig.getProxyPort();
+  }
+
+  @Override
+  public String getProxyUsername() {
+    return serviceRegistryConfig.getProxyUsername();
+  }
+
+  @Override
+  public String getProxyPassword() {
+    return serviceRegistryConfig.getProxyPasswd();
+  }
+
+  @Override
+  public boolean isSsl() {
+    return serviceRegistryConfig.isSsl();
+  }
+}
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RegistryWatchHttpClientOptionsSPI.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RegistryWatchHttpClientOptionsSPI.java
new file mode 100644
index 0000000..049de98
--- /dev/null
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RegistryWatchHttpClientOptionsSPI.java
@@ -0,0 +1,66 @@
+/*
+ * 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.client.http;
+
+import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
+
+import com.netflix.config.DynamicPropertyFactory;
+
+public class RegistryWatchHttpClientOptionsSPI extends RegistryHttpClientOptionsSPI {
+  public static final String CLIENT_NAME = "registry-watch";
+
+  private ServiceRegistryConfig serviceRegistryConfig = ServiceRegistryConfig.INSTANCE;
+
+  @Override
+  public String clientName() {
+    return CLIENT_NAME;
+  }
+
+  @Override
+  public int getOrder() {
+    // low priority than registry
+    return super.getOrder() + 1;
+  }
+
+  @Override
+  public boolean enabled() {
+    return serviceRegistryConfig.isWatch();
+  }
+
+  @Override
+  public boolean isWorker() {
+    return true;
+  }
+
+
+  @Override
+  public String getWorkerPoolName() {
+    return ServiceRegistryConfig.WORKER_POOL_NAME;
+  }
+
+  @Override
+  public int getWorkerPoolSize() {
+    return DynamicPropertyFactory.getInstance()
+        .getIntProperty(ServiceRegistryConfig.WORKER_POOL_SIZE, 4).get();
+  }
+
+  @Override
+  public boolean isProxyEnable() {
+    return false;
+  }
+}
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RestClientUtil.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RestClientUtil.java
index 6f0e8b0..bf359c6 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RestClientUtil.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RestClientUtil.java
@@ -61,7 +61,7 @@ final class RestClientUtil {
     this.authHeaderProviders = serviceRegistryConfig.getAuthHeaderProviders();
     this.requestTimeout = serviceRegistryConfig.getRequestTimeout();
     this.tenantName = serviceRegistryConfig.getTenantName();
-    this.httpClientPool = new HttpClientPool(serviceRegistryConfig);
+    this.httpClientPool = new HttpClientPool();
   }
 
   public void httpDo(RequestContext requestContext, Handler<RestResponse> responseHandler) {
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RestUtils.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RestUtils.java
deleted file mode 100644
index b902611..0000000
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/RestUtils.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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.client.http;
-
-import java.io.ByteArrayInputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.UnknownHostException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.ServiceLoader;
-
-import org.apache.servicecomb.foundation.auth.AuthHeaderProvider;
-import org.apache.servicecomb.foundation.auth.SignRequest;
-import org.apache.servicecomb.foundation.common.net.IpPort;
-import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
-import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.vertx.core.Handler;
-import io.vertx.core.MultiMap;
-import io.vertx.core.buffer.Buffer;
-import io.vertx.core.http.CaseInsensitiveHeaders;
-import io.vertx.core.http.HttpClientRequest;
-import io.vertx.core.http.HttpMethod;
-
-/**
- * This class is designed following singleton pattern, but it's not suitable for multi sc cluster occasion.
- * @deprecated consider to use {@link RestClientUtil} instead.
- */
-@Deprecated
-final class RestUtils {
-  private static final Logger LOGGER = LoggerFactory.getLogger(RestUtils.class);
-
-  private static final String HEADER_CONTENT_TYPE = "Content-Type";
-
-  private static final String HEADER_USER_AGENT = "User-Agent";
-
-  private static final String HEADER_TENANT_NAME = "x-domain-name";
-
-  private static final ServiceLoader<AuthHeaderProvider> authHeaderProviders =
-      ServiceLoader.load(AuthHeaderProvider.class);
-
-  private RestUtils() {
-  }
-
-  public static void httpDo(RequestContext requestContext, Handler<RestResponse> responseHandler) {
-    if (requestContext.getParams().getTimeout() != 0) {
-      httpDo(requestContext.getParams().getTimeout(), requestContext, responseHandler);
-      return;
-    }
-    httpDo(ServiceRegistryConfig.INSTANCE.getRequestTimeout(), requestContext, responseHandler);
-  }
-
-  public static void httpDo(long timeout, RequestContext requestContext, Handler<RestResponse> responseHandler) {
-    HttpClientWithContext vertxHttpClient = HttpClientPool.INSTANCE.getClient();
-    vertxHttpClient.runOnContext(httpClient -> {
-      IpPort ipPort = requestContext.getIpPort();
-      HttpMethod httpMethod = requestContext.getMethod();
-      RequestParam requestParam = requestContext.getParams();
-
-      if (ipPort == null) {
-        LOGGER.error("request address is null");
-        responseHandler.handle(new RestResponse(requestContext, null));
-        return;
-      }
-
-      // query params
-      StringBuilder url = new StringBuilder(requestContext.getUri());
-      String queryParams = requestParam.getQueryParams();
-      if (!queryParams.isEmpty()) {
-        url.append(url.lastIndexOf("?") > 0 ? "&" : "?")
-            .append(queryParams);
-      }
-
-      @SuppressWarnings("deprecation")
-      HttpClientRequest httpClientRequest = httpClient
-          .request(httpMethod, ipPort.getPort(), ipPort.getHostOrIp(), url.toString(), response -> {
-            responseHandler.handle(new RestResponse(requestContext, response));
-          });
-
-      httpClientRequest.setTimeout(timeout)
-          .exceptionHandler(e -> {
-            LOGGER.error("{} {} fail, endpoint is {}:{}, message: {}",
-                httpMethod,
-                url.toString(),
-                ipPort.getHostOrIp(),
-                ipPort.getPort(),
-                e.getMessage());
-            if (e instanceof UnknownHostException) {
-              // help analyses DNS problem
-              LOGGER.error("DNS resolve failed!", e);
-            }
-            responseHandler.handle(new RestResponse(requestContext, null));
-          });
-
-      //headers
-      Map<String, String> headers = defaultHeaders();
-      httpClientRequest.headers().addAll(headers);
-
-      if (requestParam.getHeaders() != null && requestParam.getHeaders().size() > 0) {
-        headers.putAll(requestParam.getHeaders());
-        for (Map.Entry<String, String> header : requestParam.getHeaders().entrySet()) {
-          httpClientRequest.putHeader(header.getKey(), header.getValue());
-        }
-      }
-
-      // cookies header
-      if (requestParam.getCookies() != null && requestParam.getCookies().size() > 0) {
-        StringBuilder stringBuilder = new StringBuilder();
-        for (Map.Entry<String, String> cookie : requestParam.getCookies().entrySet()) {
-          stringBuilder.append(cookie.getKey())
-              .append("=")
-              .append(cookie.getValue())
-              .append("; ");
-        }
-        httpClientRequest.putHeader("Cookie", stringBuilder.toString());
-        headers.put("Cookie", stringBuilder.toString());
-      }
-
-      //SignAuth
-      SignRequest signReq = createSignRequest(requestContext.getMethod().toString(),
-          requestContext.getIpPort(),
-          requestContext.getParams(),
-          url.toString(),
-          headers);
-      httpClientRequest.headers().addAll(getSignAuthHeaders(signReq));
-
-      // body
-      if (httpMethod != HttpMethod.GET && requestParam.getBody() != null && requestParam.getBody().length > 0) {
-        httpClientRequest.end(Buffer.buffer(requestParam.getBody()));
-      } else {
-        httpClientRequest.end();
-      }
-    });
-  }
-
-  public static RequestContext createRequestContext(HttpMethod method, IpPort ipPort, String uri,
-      RequestParam requestParam) {
-    RequestContext requestContext = new RequestContext();
-    requestContext.setMethod(method);
-    requestContext.setIpPort(ipPort);
-    requestContext.setUri(uri);
-    requestContext.setParams(requestParam);
-    return requestContext;
-  }
-
-  public static SignRequest createSignRequest(String method, IpPort ipPort, RequestParam requestParam, String url,
-      Map<String, String> headers) {
-    SignRequest signReq = new SignRequest();
-    StringBuilder endpoint = new StringBuilder("https://" + ipPort.getHostOrIp());
-    endpoint.append(":" + ipPort.getPort());
-    endpoint.append(url);
-    try {
-      signReq.setEndpoint(new URI(endpoint.toString()));
-    } catch (URISyntaxException e) {
-      LOGGER.error("set uri failed, uri is {}, message: {}", endpoint.toString(), e.getMessage());
-    }
-    signReq.setContent((requestParam.getBody() != null && requestParam.getBody().length > 0)
-        ? new ByteArrayInputStream(requestParam.getBody())
-        : null);
-    signReq.setHeaders(headers);
-    signReq.setHttpMethod(method);
-    signReq.setQueryParams(requestParam.getQueryParamsMap());
-    return signReq;
-  }
-
-  public static void addDefaultHeaders(HttpClientRequest request) {
-    request.headers().addAll(getDefaultHeaders());
-  }
-
-  private static Map<String, String> defaultHeaders() {
-    Map<String, String> headers = new HashMap<>();
-    headers.put(HEADER_CONTENT_TYPE, "application/json");
-    headers.put(HEADER_USER_AGENT, "cse-serviceregistry-client/1.0.0");
-    headers.put(HEADER_TENANT_NAME, ServiceRegistryConfig.INSTANCE.getTenantName());
-
-    return headers;
-  }
-
-  public static MultiMap getDefaultHeaders() {
-    return new CaseInsensitiveHeaders().addAll(defaultHeaders());
-  }
-
-  public static void get(IpPort ipPort, String uri, RequestParam requestParam,
-      Handler<RestResponse> responseHandler) {
-    httpDo(createRequestContext(HttpMethod.GET, ipPort, uri, requestParam), responseHandler);
-  }
-
-  public static void post(IpPort ipPort, String uri, RequestParam requestParam,
-      Handler<RestResponse> responseHandler) {
-    httpDo(createRequestContext(HttpMethod.POST, ipPort, uri, requestParam), responseHandler);
-  }
-
-  public static void put(IpPort ipPort, String uri, RequestParam requestParam,
-      Handler<RestResponse> responseHandler) {
-    httpDo(createRequestContext(HttpMethod.PUT, ipPort, uri, requestParam), responseHandler);
-  }
-
-  public static void delete(IpPort ipPort, String uri, RequestParam requestParam,
-      Handler<RestResponse> responseHandler) {
-    httpDo(createRequestContext(HttpMethod.DELETE, ipPort, uri, requestParam), responseHandler);
-  }
-
-  public static Map<String, String> getSignAuthHeaders(SignRequest signReq) {
-    Map<String, String> headers = new HashMap<>();
-    authHeaderProviders.forEach(provider -> headers.putAll(provider.getSignAuthHeaders(signReq)));
-    return headers;
-  }
-}
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientPool.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientPool.java
index 96ab64f..6a17fb3 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientPool.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientPool.java
@@ -17,51 +17,15 @@
 
 package org.apache.servicecomb.serviceregistry.client.http;
 
-import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder;
-import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.vertx.core.http.HttpClientOptions;
-import io.vertx.core.http.HttpVersion;
-
 public class WebsocketClientPool extends AbstractClientPool {
 
-  private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketClientPool.class);
-
   /**
    * The default instance, for default sc cluster.
    */
   public static final WebsocketClientPool INSTANCE = new WebsocketClientPool();
 
-  private WebsocketClientPool() {
-    super(ServiceRegistryConfig.INSTANCE);
-  }
-
-  WebsocketClientPool(ServiceRegistryConfig serviceRegistryConfig) {
-    super(serviceRegistryConfig);
-  }
-
-  @Override
-  protected boolean isWorker() {
-    return true;
-  }
-
   @Override
-  protected HttpClientOptions getHttpClientOptionsFromConfigurations(ServiceRegistryConfig serviceRegistryConfig) {
-    HttpVersion ver = serviceRegistryConfig.getHttpVersion();
-    HttpClientOptions httpClientOptions = new HttpClientOptions();
-    httpClientOptions.setProtocolVersion(ver);
-    httpClientOptions.setConnectTimeout(serviceRegistryConfig.getConnectionTimeout());
-    httpClientOptions.setIdleTimeout(serviceRegistryConfig.getIdleWatchTimeout());
-    if (ver == HttpVersion.HTTP_2) {
-      LOGGER.debug("service center ws client protocol version is HTTP/2");
-      httpClientOptions.setHttp2ClearTextUpgrade(false);
-    }
-    if (serviceRegistryConfig.isSsl()) {
-      LOGGER.debug("service center ws client performs requests over TLS");
-      VertxTLSBuilder.buildHttpClientOptions(serviceRegistryConfig.getSslConfigTag(), httpClientOptions);
-    }
-    return httpClientOptions;
+  public String getName() {
+    return RegistryWatchHttpClientOptionsSPI.CLIENT_NAME;
   }
 }
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientUtil.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientUtil.java
index 9262752..237b231 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientUtil.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketClientUtil.java
@@ -47,7 +47,7 @@ public final class WebsocketClientUtil {
   private List<AuthHeaderProvider> authHeaderProviders;
 
   WebsocketClientUtil(ServiceRegistryConfig serviceRegistryConfig) {
-    websocketClientPool = new WebsocketClientPool(serviceRegistryConfig);
+    websocketClientPool = new WebsocketClientPool();
     authHeaderProviders = serviceRegistryConfig.getAuthHeaderProviders();
   }
 
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketUtils.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketUtils.java
deleted file mode 100644
index d7427ff..0000000
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/http/WebsocketUtils.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.client.http;
-
-import java.util.HashMap;
-
-import org.apache.servicecomb.foundation.common.net.IpPort;
-import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.vertx.core.Handler;
-import io.vertx.core.buffer.Buffer;
-import io.vertx.core.http.HttpMethod;
-import io.vertx.core.http.WebSocketConnectOptions;
-
-/**
- * This class is designed following singleton pattern, but it's not suitable for multi sc cluster occasion.
- * @deprecated consider to use {@link WebsocketClientUtil} instead.
- */
-@Deprecated
-public final class WebsocketUtils {
-  private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketUtils.class);
-
-  private WebsocketUtils() {
-  }
-
-  public static void open(IpPort ipPort, String url, Handler<Void> onOpen, Handler<Void> onClose,
-      Handler<Buffer> onMessage, Handler<Throwable> onException,
-      Handler<Throwable> onConnectFailed) {
-    HttpClientWithContext vertxHttpClient = WebsocketClientPool.INSTANCE.getClient();
-    vertxHttpClient.runOnContext(client -> {
-      WebSocketConnectOptions options = new WebSocketConnectOptions();
-      options.setHost(ipPort.getHostOrIp()).setPort(ipPort.getPort()).setURI(url)
-          .setHeaders(RestUtils.getDefaultHeaders().addAll(RestUtils.getSignAuthHeaders(
-              RestUtils.createSignRequest(HttpMethod.GET.name(), ipPort, new RequestParam(), url, new HashMap<>()))));
-      client.webSocket(options, asyncResult -> {
-        if (asyncResult.failed()) {
-          onConnectFailed.handle(asyncResult.cause());
-        } else {
-          onOpen.handle(null);
-          asyncResult.result().exceptionHandler(v -> {
-            onException.handle(v);
-            try {
-              asyncResult.result().close();
-            } catch (Exception err) {
-              LOGGER.error("ws close error.", err);
-            }
-          });
-          asyncResult.result().closeHandler(v -> {
-            onClose.handle(v);
-          });
-          asyncResult.result().pongHandler(pong -> {
-            // ignore, just prevent NPE.
-          });
-          asyncResult.result().frameHandler((frame) -> onMessage.handle(frame.binaryData()));
-        }
-      });
-    });
-  }
-}
diff --git a/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI b/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI
new file mode 100644
index 0000000..b1ab72d
--- /dev/null
+++ b/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.servicecomb.serviceregistry.client.http.RegistryHttpClientOptionsSPI
+org.apache.servicecomb.serviceregistry.client.http.RegistryWatchHttpClientOptionsSPI
diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/RestUtilsTest.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/RestUtilsTest.java
deleted file mode 100644
index 336a805..0000000
--- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/RestUtilsTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.client.http;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
-
-import java.util.HashMap;
-
-import org.apache.servicecomb.foundation.common.net.IpPort;
-import org.junit.Test;
-
-import io.vertx.core.MultiMap;
-import io.vertx.core.http.HttpMethod;
-
-@SuppressWarnings("deprecation")
-public class RestUtilsTest {
-  @Test
-  public void defaultHeadersContainServiceRegistryAndAuthentication() throws Exception {
-
-    MultiMap headers = RestUtils.getDefaultHeaders();
-    headers.addAll(RestUtils.getSignAuthHeaders(RestUtils.createSignRequest(HttpMethod.GET.toString(),
-        new IpPort("127.0.0.1", 443),
-        new RequestParam().addQueryParam("testParam", "test"),
-        "test",
-        new HashMap<>())));
-    assertThat(headers.get("Content-Type"), is("application/json"));
-    assertThat(headers.get("User-Agent"), is("cse-serviceregistry-client/1.0.0"));
-    assertThat(headers.get("x-domain-name"), is("default"));
-    assertThat(headers.get("X-Service-AK"), is("blah..."));
-  }
-}
diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestAbstractClientPool.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestAbstractClientPool.java
index 937d67e..fee4b6b 100644
--- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestAbstractClientPool.java
+++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestAbstractClientPool.java
@@ -18,12 +18,8 @@ package org.apache.servicecomb.serviceregistry.client.http;
 
 import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
 import org.apache.servicecomb.foundation.vertx.VertxUtils;
-import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager;
-import org.apache.servicecomb.foundation.vertx.client.ClientVerticle;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Test;
 
 import io.vertx.core.AbstractVerticle;
 import io.vertx.core.DeploymentOptions;
@@ -73,16 +69,4 @@ public class TestAbstractClientPool {
   public void teardown() {
     ArchaiusUtils.resetConfig();
   }
-
-  @Test
-  public void create() {
-    HttpClientPool.INSTANCE.create();
-
-    Assert.assertEquals("registry", vertxName);
-
-    Assert.assertEquals(ClientVerticle.class, verticleCls);
-    Assert.assertEquals(ClientPoolManager.class,
-        deployOptions.getConfig().getValue(ClientVerticle.CLIENT_MGR).getClass());
-    System.out.println(deployOptions);
-  }
 }
diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestHttpClientPool.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestHttpClientPool.java
index 4b57d61..cf5a62a 100644
--- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestHttpClientPool.java
+++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestHttpClientPool.java
@@ -18,14 +18,8 @@
 package org.apache.servicecomb.serviceregistry.client.http;
 
 import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
-import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Test;
-
-import io.vertx.core.http.HttpClientOptions;
-import io.vertx.core.http.HttpVersion;
 
 public class TestHttpClientPool {
   @Before
@@ -37,72 +31,4 @@ public class TestHttpClientPool {
   public void teardown() {
     ArchaiusUtils.resetConfig();
   }
-
-  @Test
-  public void createHttpClientOptions_proxy() {
-    ArchaiusUtils.setProperty(ServiceRegistryConfig.PROXY_ENABLE, "true");
-    ArchaiusUtils.setProperty(ServiceRegistryConfig.PROXY_HOST, "host");
-    ArchaiusUtils.setProperty(ServiceRegistryConfig.PROXY_PORT, "1234");
-    ArchaiusUtils.setProperty(ServiceRegistryConfig.PROXY_USERNAME, "user");
-    ArchaiusUtils.setProperty(ServiceRegistryConfig.PROXY_PASSWD, "pass");
-
-    HttpClientOptions httpClientOptions = new HttpClientPool(ServiceRegistryConfig.buildFromConfiguration()) {
-      @Override
-      public void create() {
-      }
-    }.getHttpClientOptionsFromConfigurations(
-        ServiceRegistryConfig.buildFromConfiguration());
-
-    Assert.assertEquals(
-        "{"
-            + "\"host\":\"host\","
-            + "\"password\":\"pass\","
-            + "\"port\":1234,"
-            + "\"type\":\"HTTP\","
-            + "\"username\":\"user\""
-            + "}",
-        httpClientOptions.getProxyOptions().toJson().toString());
-  }
-
-  @Test
-  public void createHttpClientOptions_noProxy() {
-    ArchaiusUtils.setProperty(ServiceRegistryConfig.PROXY_ENABLE, "false");
-
-    HttpClientOptions httpClientOptions = new HttpClientPool(ServiceRegistryConfig.buildFromConfiguration()) {
-      @Override
-      public void create() {
-      }
-    }.getHttpClientOptionsFromConfigurations(
-        ServiceRegistryConfig.buildFromConfiguration());
-
-    Assert.assertNull(httpClientOptions.getProxyOptions());
-  }
-
-  @Test
-  public void createHttpClientOptions_http2() {
-    ArchaiusUtils.setProperty("servicecomb.service.registry.client.httpVersion", HttpVersion.HTTP_2.name());
-
-    HttpClientOptions httpClientOptions = new HttpClientPool(ServiceRegistryConfig.buildFromConfiguration()) {
-      @Override
-      public void create() {
-      }
-    }.getHttpClientOptionsFromConfigurations(
-        ServiceRegistryConfig.buildFromConfiguration());
-
-    Assert.assertEquals(HttpVersion.HTTP_2, httpClientOptions.getProtocolVersion());
-    Assert.assertFalse(httpClientOptions.isHttp2ClearTextUpgrade());
-  }
-
-  @Test
-  public void createHttpClientOptions_notHttp2() {
-    HttpClientOptions httpClientOptions = new HttpClientPool(ServiceRegistryConfig.buildFromConfiguration()) {
-      @Override
-      public void create() {
-      }
-    }.getHttpClientOptionsFromConfigurations(
-        ServiceRegistryConfig.buildFromConfiguration());
-
-    Assert.assertEquals(HttpVersion.HTTP_1_1, httpClientOptions.getProtocolVersion());
-    Assert.assertTrue(httpClientOptions.isHttp2ClearTextUpgrade());
-  }
 }
diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java
index 5c6384c..d065c59 100644
--- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java
+++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java
@@ -33,6 +33,7 @@ import org.apache.log4j.Appender;
 import org.apache.log4j.Logger;
 import org.apache.log4j.spi.LoggingEvent;
 import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
 import org.apache.servicecomb.serviceregistry.RegistryUtils;
 import org.apache.servicecomb.serviceregistry.api.registry.Microservice;
 import org.apache.servicecomb.serviceregistry.api.registry.MicroserviceFactory;
@@ -74,6 +75,8 @@ public class TestServiceRegistryClientImpl {
 
   @Before
   public void setUp() throws Exception {
+    HttpClients.load();
+
     oClient = new ServiceRegistryClientImpl(ServiceRegistryConfig.buildFromConfiguration());
 
     new MockUp<RegistryUtils>() {
@@ -93,6 +96,7 @@ public class TestServiceRegistryClientImpl {
   @After
   public void tearDown() throws Exception {
     oClient = null;
+    HttpClients.destroy();
   }
 
   @Test
diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestWebsocketClientPool.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestWebsocketClientPool.java
index 93c0453..5427c53 100644
--- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestWebsocketClientPool.java
+++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestWebsocketClientPool.java
@@ -17,14 +17,8 @@
 package org.apache.servicecomb.serviceregistry.client.http;
 
 import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
-import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Test;
-
-import io.vertx.core.http.HttpClientOptions;
-import io.vertx.core.http.HttpVersion;
 
 public class TestWebsocketClientPool {
   @Before
@@ -36,32 +30,4 @@ public class TestWebsocketClientPool {
   public void teardown() {
     ArchaiusUtils.resetConfig();
   }
-
-  @Test
-  public void createHttpClientOptions_http2() {
-    ArchaiusUtils.setProperty("servicecomb.service.registry.client.httpVersion", HttpVersion.HTTP_2.name());
-
-    HttpClientOptions httpClientOptions = new WebsocketClientPool(ServiceRegistryConfig.buildFromConfiguration()) {
-      @Override
-      public void create() {
-      }
-    }.getHttpClientOptionsFromConfigurations(
-        ServiceRegistryConfig.buildFromConfiguration());
-
-    Assert.assertEquals(HttpVersion.HTTP_2, httpClientOptions.getProtocolVersion());
-    Assert.assertFalse(httpClientOptions.isHttp2ClearTextUpgrade());
-  }
-
-  @Test
-  public void createHttpClientOptions_notHttp2() {
-    HttpClientOptions httpClientOptions = new WebsocketClientPool(ServiceRegistryConfig.buildFromConfiguration()) {
-      @Override
-      public void create() {
-      }
-    }.getHttpClientOptionsFromConfigurations(
-        ServiceRegistryConfig.buildFromConfiguration());
-
-    Assert.assertEquals(HttpVersion.HTTP_1_1, httpClientOptions.getProtocolVersion());
-    Assert.assertTrue(httpClientOptions.isHttp2ClearTextUpgrade());
-  }
 }