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 92a8490  [SCB-2218] allow edge invocation process by filter (#2297)
92a8490 is described below

commit 92a8490b661c86637cf7cb7e04b14231863dec27
Author: wujimin <[email protected]>
AuthorDate: Thu Mar 18 20:25:25 2021 +0800

    [SCB-2218] allow edge invocation process by filter (#2297)
---
 .../common/rest/RestProducerInvocationCreator.java | 32 +++++---
 .../rest/RestVertxProducerInvocationCreator.java   |  2 +-
 .../rest/filter/inner/RestServerCodecFilter.java   | 15 ++--
 .../rest/RestProducerInvocationCreatorTest.java    | 10 +--
 .../filter/inner/RestServerCodecFilterTest.java    |  6 +-
 .../org/apache/servicecomb/core/Invocation.java    |  4 +
 .../core/invocation/InvocationCreator.java         |  4 +-
 .../core/invocation/ProducerInvocationFlow.java    |  6 +-
 .../invocation/ProducerInvocationFlowTest.java     | 11 +--
 .../edge/core/CompatiblePathVersionMapper.java     |  4 +-
 .../edge/core/DefaultEdgeDispatcher.java           | 75 ++++++++++++++---
 .../edge/core/EdgeAddHeaderClientFilter.java       | 34 +++++---
 .../servicecomb/edge/core/EdgeAddHeaderFilter.java | 54 ++++++++++++
 .../edge/core/EdgeInvocationCreator.java           | 96 ++++++++++++++++++++++
 .../edge/core/EdgeRestTransportClient.java         | 13 +--
 .../edge/core/EdgeServerCodecFilter.java           | 66 +++++++++++++++
 .../edge/core/TestDefaultEdgeDispatcher.java       | 25 +++---
 .../it-edge/src/main/resources/microservice.yaml   |  6 ++
 .../servicecomb/registry/lightweight/Self.java     |  8 +-
 .../transport/highway/HighwayServerConnection.java |  6 +-
 .../transport/rest/vertx/RestServerVerticle.java   |  6 +-
 .../transport/rest/vertx/VertxHttpDispatcher.java  |  8 +-
 22 files changed, 397 insertions(+), 94 deletions(-)

diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java
index 69eb467..0cbdc15 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java
@@ -22,6 +22,7 @@ import static 
org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIEN
 import static 
org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA;
 
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 
 import javax.annotation.Nonnull;
 import javax.ws.rs.core.HttpHeaders;
@@ -48,7 +49,7 @@ import io.vertx.core.json.Json;
 public abstract class RestProducerInvocationCreator implements 
InvocationCreator {
   private static final Logger LOGGER = 
LoggerFactory.getLogger(RestVertxProducerInvocationCreator.class);
 
-  protected final MicroserviceMeta microserviceMeta;
+  protected MicroserviceMeta microserviceMeta;
 
   protected final Endpoint endpoint;
 
@@ -56,11 +57,11 @@ public abstract class RestProducerInvocationCreator 
implements InvocationCreator
 
   protected final HttpServletResponseEx responseEx;
 
-  private RestOperationMeta restOperationMeta;
+  protected RestOperationMeta restOperationMeta;
 
   protected ProduceProcessor produceProcessor;
 
-  public RestProducerInvocationCreator(@Nonnull MicroserviceMeta 
microserviceMeta, @Nonnull Endpoint endpoint,
+  public RestProducerInvocationCreator(MicroserviceMeta microserviceMeta, 
Endpoint endpoint,
       @Nonnull HttpServletRequestEx requestEx, @Nonnull HttpServletResponseEx 
responseEx) {
     this.microserviceMeta = microserviceMeta;
     this.endpoint = endpoint;
@@ -69,12 +70,10 @@ public abstract class RestProducerInvocationCreator 
implements InvocationCreator
   }
 
   @Override
-  public Invocation create() {
+  public CompletableFuture<Invocation> createAsync() {
     initRestOperation();
 
-    Invocation invocation = InvocationFactory.forProvider(endpoint,
-        restOperationMeta.getOperationMeta(),
-        null);
+    Invocation invocation = createInstance();
     initInvocationContext(invocation);
 
     initProduceProcessor();
@@ -82,7 +81,11 @@ public abstract class RestProducerInvocationCreator 
implements InvocationCreator
 
     invocation.addLocalContext(RestConst.REST_REQUEST, requestEx);
 
-    return invocation;
+    return CompletableFuture.completedFuture(invocation);
+  }
+
+  protected Invocation createInstance() {
+    return InvocationFactory.forProvider(endpoint, 
restOperationMeta.getOperationMeta(), null);
   }
 
   protected void initInvocationContext(Invocation invocation) {
@@ -98,19 +101,24 @@ public abstract class RestProducerInvocationCreator 
implements InvocationCreator
 
   protected abstract void initTransportContext(Invocation invocation);
 
-  private void initRestOperation() {
-    OperationLocator locator = locateOperation();
+  protected void initRestOperation() {
+    OperationLocator locator = locateOperation(microserviceMeta);
     requestEx.setAttribute(RestConst.PATH_PARAMETERS, locator.getPathVarMap());
     restOperationMeta = locator.getOperation();
   }
 
-  private OperationLocator locateOperation() {
+  protected OperationLocator locateOperation(MicroserviceMeta 
microserviceMeta) {
     ServicePathManager servicePathManager = 
ServicePathManager.getServicePathManager(microserviceMeta);
     if (servicePathManager == null) {
-      LOGGER.error("No schema defined for {}:{}.", 
microserviceMeta.getAppId(), microserviceMeta.getMicroserviceName());
+      LOGGER.error("No schema defined for {}:{}.", 
this.microserviceMeta.getAppId(),
+          this.microserviceMeta.getMicroserviceName());
       throw Exceptions.create(NOT_FOUND, NOT_DEFINED_ANY_SCHEMA, 
NOT_FOUND.getReasonPhrase());
     }
 
+    return locateOperation(servicePathManager);
+  }
+
+  protected OperationLocator locateOperation(ServicePathManager 
servicePathManager) {
     return 
servicePathManager.producerLocateOperation(requestEx.getRequestURI(), 
requestEx.getMethod());
   }
 
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java
index 4f12dea..fcae4c7 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java
@@ -30,7 +30,7 @@ public class RestVertxProducerInvocationCreator extends 
RestProducerInvocationCr
   private final RoutingContext routingContext;
 
   public RestVertxProducerInvocationCreator(@Nonnull RoutingContext 
routingContext,
-      @Nonnull MicroserviceMeta microserviceMeta, @Nonnull Endpoint endpoint,
+      MicroserviceMeta microserviceMeta, Endpoint endpoint,
       @Nonnull HttpServletRequestEx requestEx, @Nonnull HttpServletResponseEx 
responseEx) {
     super(microserviceMeta, endpoint, requestEx, responseEx);
     this.routingContext = routingContext;
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
index 3dd5557..9d8894b 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
@@ -61,22 +61,25 @@ public class RestServerCodecFilter implements 
ProducerFilter {
   @Override
   public CompletableFuture<Response> onFilter(Invocation invocation, 
FilterNode nextNode) {
     return CompletableFuture.completedFuture(invocation)
-        .thenCompose(this::decodeRequest)
-        .thenCompose(nextNode::onFilter)
+        .thenAccept(this::decodeRequest)
+        .thenCompose(v -> invokeNext(invocation, nextNode))
         .exceptionally(exception -> exceptionToResponse(invocation, exception, 
INTERNAL_SERVER_ERROR))
         .thenCompose(response -> encodeResponse(invocation, response));
   }
 
-  protected CompletableFuture<Invocation> decodeRequest(Invocation invocation) 
{
-    HttpTransportContext transportContext = invocation.getTransportContext();
-    HttpServletRequestEx requestEx = transportContext.getRequestEx();
+  protected CompletableFuture<Response> invokeNext(Invocation invocation, 
FilterNode nextNode) {
+    return nextNode.onFilter(invocation);
+  }
+
+  protected Void decodeRequest(Invocation invocation) {
+    HttpServletRequestEx requestEx = invocation.getRequestEx();
 
     OperationMeta operationMeta = invocation.getOperationMeta();
     RestOperationMeta restOperationMeta = 
operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
     Map<String, Object> swaggerArguments = RestCodec.restToArgs(requestEx, 
restOperationMeta);
     invocation.setSwaggerArguments(swaggerArguments);
 
-    return CompletableFuture.completedFuture(invocation);
+    return null;
   }
 
   protected CompletableFuture<Response> encodeResponse(Invocation invocation, 
Response response) {
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
index b1dc7ff..f8159a6 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
@@ -115,7 +115,7 @@ public class RestProducerInvocationCreatorTest {
   public void should_failed_when_not_defined_any_schema() {
     mockGetServicePathManager(null);
 
-    InvocationException throwable = (InvocationException) catchThrowable(() -> 
creator.create());
+    InvocationException throwable = (InvocationException) catchThrowable(() -> 
creator.createAsync().join());
     CommonExceptionData data = (CommonExceptionData) throwable.getErrorData();
 
     assertThat(throwable.getStatusCode()).isEqualTo(NOT_FOUND.getStatusCode());
@@ -135,7 +135,7 @@ public class RestProducerInvocationCreatorTest {
       }
     };
 
-    InvocationException throwable = (InvocationException) catchThrowable(() -> 
creator.create());
+    InvocationException throwable = (InvocationException) catchThrowable(() -> 
creator.createAsync().join());
     CommonExceptionData data = (CommonExceptionData) throwable.getErrorData();
 
     
assertThat(throwable.getStatusCode()).isEqualTo(NOT_ACCEPTABLE.getStatusCode());
@@ -147,7 +147,7 @@ public class RestProducerInvocationCreatorTest {
   public void should_save_requestEx_in_invocation_context() {
     mockGetServicePathManager();
 
-    Invocation invocation = creator.create();
+    Invocation invocation = creator.createAsync().join();
 
     Object request = invocation.getLocalContext(RestConst.REST_REQUEST);
     assertThat(request).isSameAs(requestEx);
@@ -157,7 +157,7 @@ public class RestProducerInvocationCreatorTest {
   public void should_save_path_var_map_in_requestEx() {
     mockGetServicePathManager();
 
-    creator.create();
+    creator.createAsync().join();
 
     new Verifications() {
       {
@@ -177,7 +177,7 @@ public class RestProducerInvocationCreatorTest {
       }
     };
 
-    Invocation invocation = creator.create();
+    Invocation invocation = creator.createAsync().join();
 
     assertThat(invocation.getContext("k")).isEqualTo("v");
   }
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
index 8b887b6..24942a0 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
@@ -112,8 +112,8 @@ public class RestServerCodecFilterTest {
         invocation.getTransportContext();
         result = transportContext;
 
-        transportContext.getRequestEx();
-        result = new RuntimeExceptionWithoutStackTrace("encode request 
failed");
+        invocation.getRequestEx();
+        result = new RuntimeExceptionWithoutStackTrace("mock encode request 
failed");
       }
     };
   }
@@ -147,7 +147,7 @@ public class RestServerCodecFilterTest {
 
     assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR);
     assertThat(Json.encode(response.getResult()))
-        .isEqualTo("{\"code\":\"SCB.50000000\",\"message\":\"encode request 
failed\"}");
+        .isEqualTo("{\"code\":\"SCB.50000000\",\"message\":\"mock encode 
request failed\"}");
   }
 
   private void success_invocation() throws InterruptedException, 
ExecutionException {
diff --git a/core/src/main/java/org/apache/servicecomb/core/Invocation.java 
b/core/src/main/java/org/apache/servicecomb/core/Invocation.java
index ae35727..88f42bc 100644
--- a/core/src/main/java/org/apache/servicecomb/core/Invocation.java
+++ b/core/src/main/java/org/apache/servicecomb/core/Invocation.java
@@ -458,6 +458,10 @@ public class Invocation extends SwaggerInvocation {
     return InvocationType.CONSUMER.equals(invocationType);
   }
 
+  public boolean isProducer() {
+    return InvocationType.PRODUCER.equals(invocationType);
+  }
+
   public boolean isEdge() {
     return edge;
   }
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java
 
b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java
index 5b86c3b..a413fce 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java
@@ -16,11 +16,13 @@
  */
 package org.apache.servicecomb.core.invocation;
 
+import java.util.concurrent.CompletableFuture;
+
 import org.apache.servicecomb.core.Invocation;
 
 /**
  * better to named InvocationFactory, but already be used by old version
  */
 public interface InvocationCreator {
-  Invocation create();
+  CompletableFuture<Invocation> createAsync();
 }
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
 
b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
index 93af06c..6f4f8e2 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
@@ -16,9 +16,10 @@
  */
 package org.apache.servicecomb.core.invocation;
 
+import java.util.concurrent.CompletableFuture;
+
 import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.core.exception.Exceptions;
-import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
 import org.apache.servicecomb.foundation.common.utils.ExceptionUtils;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
@@ -49,7 +50,8 @@ public abstract class ProducerInvocationFlow {
   }
 
   public void run() {
-    AsyncUtils.tryCatchSupplier(invocationCreator::create)
+    CompletableFuture.completedFuture(null)
+        .thenCompose(v -> invocationCreator.createAsync())
         .exceptionally(this::sendCreateInvocationException)
         .thenAccept(this::tryRunInvocation);
   }
diff --git 
a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
 
b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
index b8c0d46..f1d3ef7 100644
--- 
a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
+++ 
b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.servicecomb.core.invocation;
 
+import static java.util.concurrent.CompletableFuture.completedFuture;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.servicecomb.core.Invocation;
@@ -87,7 +88,7 @@ public class ProducerInvocationFlowTest {
   @Test
   public void should_start_invocation_when_succeed_to_create_invocation() {
     mockFilterChain();
-    TestFlow flow = new TestFlow(() -> invocation);
+    TestFlow flow = new TestFlow(() -> completedFuture(invocation));
 
     flow.run();
 
@@ -102,7 +103,7 @@ public class ProducerInvocationFlowTest {
   @Test
   public void should_send_response_when_invocation_success() {
     mockFilterChain();
-    TestFlow flow = new TestFlow(() -> invocation);
+    TestFlow flow = new TestFlow(() -> completedFuture(invocation));
 
     flow.run();
 
@@ -112,7 +113,7 @@ public class ProducerInvocationFlowTest {
   @Test
   public void should_finish_invocation_when_invocation_success() {
     mockFilterChain();
-    TestFlow flow = new TestFlow(() -> invocation);
+    TestFlow flow = new TestFlow(() -> completedFuture(invocation));
 
     flow.run();
 
@@ -134,7 +135,7 @@ public class ProducerInvocationFlowTest {
   @Test
   public void should_send_response_when_invocation_fail() {
     mockInvocationFailed();
-    TestFlow flow = new TestFlow(() -> invocation);
+    TestFlow flow = new TestFlow(() -> completedFuture(invocation));
 
     flow.run();
 
@@ -144,7 +145,7 @@ public class ProducerInvocationFlowTest {
   @Test
   public void should_finish_invocation_when_invocation_fail() {
     mockInvocationFailed();
-    TestFlow flow = new TestFlow(() -> invocation);
+    TestFlow flow = new TestFlow(() -> completedFuture(invocation));
 
     flow.run();
 
diff --git 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CompatiblePathVersionMapper.java
 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CompatiblePathVersionMapper.java
index 599bc32..cb92aec 100644
--- 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CompatiblePathVersionMapper.java
+++ 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CompatiblePathVersionMapper.java
@@ -31,9 +31,7 @@ public class CompatiblePathVersionMapper {
   private Map<String, VersionRule> mapper = new ConcurrentHashMapEx<>();
 
   public VersionRule getOrCreate(String pathVersion) {
-    return mapper.computeIfAbsent(pathVersion, pv -> {
-      return createVersionRule(pathVersion);
-    });
+    return mapper.computeIfAbsent(pathVersion, this::createVersionRule);
   }
 
   // v + number
diff --git 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java
 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java
index 19f9f65..e85fb28 100644
--- 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java
+++ 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java
@@ -17,10 +17,19 @@
 
 package org.apache.servicecomb.edge.core;
 
-import java.util.Map;
-
+import org.apache.servicecomb.common.rest.RestProducerInvocationFlow;
+import org.apache.servicecomb.core.SCBEngine;
+import org.apache.servicecomb.core.invocation.InvocationCreator;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
+import 
org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest;
+import 
org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse;
+import org.apache.servicecomb.registry.definition.DefinitionConst;
+
+import com.google.common.annotations.VisibleForTesting;
 import com.netflix.config.DynamicPropertyFactory;
 
+import io.vertx.codegen.annotations.Nullable;
 import io.vertx.ext.web.Router;
 import io.vertx.ext.web.RoutingContext;
 
@@ -28,6 +37,7 @@ import io.vertx.ext.web.RoutingContext;
  * Provide an easy mapping dispatcher that starts with a common prefix pattern.
  */
 public class DefaultEdgeDispatcher extends AbstractEdgeDispatcher {
+
   private static final String KEY_ENABLED = 
"servicecomb.http.dispatcher.edge.default.enabled";
 
   private static final String KEY_ORDER = 
"servicecomb.http.dispatcher.edge.default.order";
@@ -38,6 +48,10 @@ public class DefaultEdgeDispatcher extends 
AbstractEdgeDispatcher {
 
   private static final String KEY_PREFIX_SEGMENT_COUNT = 
"servicecomb.http.dispatcher.edge.default.prefixSegmentCount";
 
+  public static final String MICROSERVICE_NAME = "param0";
+
+  public static final String VERSION = "param1";
+
   private CompatiblePathVersionMapper versionMapper = new 
CompatiblePathVersionMapper();
 
   private String prefix;
@@ -61,27 +75,62 @@ public class DefaultEdgeDispatcher extends 
AbstractEdgeDispatcher {
     prefix = 
DynamicPropertyFactory.getInstance().getStringProperty(KEY_PREFIX, "api").get();
     withVersion = 
DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_WITH_VERSION, 
true).get();
     prefixSegmentCount = 
DynamicPropertyFactory.getInstance().getIntProperty(KEY_PREFIX_SEGMENT_COUNT, 
1).get();
-    String regex;
-    if (withVersion) {
-      regex = "/" + prefix + "/([^\\\\/]+)/([^\\\\/]+)/(.*)";
-    } else {
-      regex = "/" + prefix + "/([^\\\\/]+)/(.*)";
-    }
+    String regex = generateRouteRegex(prefix, withVersion);
+
     // cookies handler are enabled by default start from 3.8.3
     router.routeWithRegex(regex).handler(createBodyHandler());
     
router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest);
   }
 
+  @VisibleForTesting
+  String generateRouteRegex(String prefix, boolean withVersion) {
+    String version = withVersion ? "/([^\\\\/]+)" : "";
+    return String.format("/%s/([^\\\\/]+)%s/(.*)", prefix, version);
+  }
+
   protected void onRequest(RoutingContext context) {
-    Map<String, String> pathParams = context.pathParams();
-    String microserviceName = pathParams.get("param0");
+    String microserviceName = extractMicroserviceName(context);
+    String versionRule = extractVersionRule(context);
     String path = Utils.findActualPath(context.request().path(), 
prefixSegmentCount);
 
-    EdgeInvocation edgeInvocation =  createEdgeInvocation();
+    if (isFilterChainEnabled()) {
+      requestByFilter(context, microserviceName, versionRule, path);
+      return;
+    }
+
+    requestByHandler(context, microserviceName, versionRule, path);
+  }
+
+  protected boolean isFilterChainEnabled() {
+    return SCBEngine.getInstance().isFilterChainEnabled();
+  }
+
+  @Nullable
+  private String extractMicroserviceName(RoutingContext context) {
+    return context.pathParam(MICROSERVICE_NAME);
+  }
+
+  private String extractVersionRule(RoutingContext context) {
     if (withVersion) {
-      String pathVersion = pathParams.get("param1");
-      
edgeInvocation.setVersionRule(versionMapper.getOrCreate(pathVersion).getVersionRule());
+      String pathVersion = context.pathParam(VERSION);
+      return versionMapper.getOrCreate(pathVersion).getVersionRule();
     }
+
+    return DefinitionConst.VERSION_RULE_ALL;
+  }
+
+  protected void requestByFilter(RoutingContext context, String 
microserviceName, String versionRule, String path) {
+    HttpServletRequestEx requestEx = new 
VertxServerRequestToHttpServletRequest(context);
+    HttpServletResponseEx responseEx = new 
VertxServerResponseToHttpServletResponse(context.response());
+    InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, 
responseEx,
+        microserviceName, versionRule, path);
+    new RestProducerInvocationFlow(creator, requestEx, responseEx)
+        .run();
+  }
+
+  private void requestByHandler(RoutingContext context, String 
microserviceName, String versionRule, String path) {
+    EdgeInvocation edgeInvocation = createEdgeInvocation();
+    edgeInvocation.setVersionRule(versionRule);
     edgeInvocation.init(microserviceName, context, path, httpServerFilters);
     edgeInvocation.edgeInvoke();
   }
diff --git 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderClientFilter.java
 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderClientFilter.java
index 9a2632f..95b67dd 100644
--- 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderClientFilter.java
+++ 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderClientFilter.java
@@ -19,6 +19,7 @@ package org.apache.servicecomb.edge.core;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.BiConsumer;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.servicecomb.common.rest.filter.HttpClientFilter;
@@ -29,15 +30,17 @@ import org.apache.servicecomb.swagger.invocation.Response;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.netflix.config.ConcurrentCompositeConfiguration;
+import com.netflix.config.ConfigurationManager;
 import com.netflix.config.DynamicPropertyFactory;
 
 public class EdgeAddHeaderClientFilter implements HttpClientFilter {
   private static final Logger LOGGER = 
LoggerFactory.getLogger(EdgeAddHeaderClientFilter.class);
 
-  private static final String KEY_ENABLED = 
"servicecomb.edge.filter.addHeader.enabled";
+  private static final String PREFIX = "servicecomb.edge.filter.addHeader";
 
-  private static final String KEY_HEADERS = 
"servicecomb.edge.filter.addHeader.allowedHeaders";
+  private static final String KEY_ENABLED = PREFIX + ".enabled";
+
+  private static final String KEY_HEADERS = PREFIX + ".allowedHeaders";
 
   private List<String> publicHeaders = new ArrayList<>();
 
@@ -45,13 +48,13 @@ public class EdgeAddHeaderClientFilter implements 
HttpClientFilter {
 
   public EdgeAddHeaderClientFilter() {
     init();
-    ((ConcurrentCompositeConfiguration) DynamicPropertyFactory
-        .getBackingConfigurationSource()).addConfigurationListener(event -> {
-      if (event.getPropertyName().startsWith(KEY_HEADERS) || 
event.getPropertyName().startsWith(KEY_ENABLED)) {
-        LOGGER.info("Public headers config have been changed. Event=" + 
event.getType());
-        init();
-      }
-    });
+    ConfigurationManager.getConfigInstance()
+        .addConfigurationListener(event -> {
+          if (StringUtils.startsWith(event.getPropertyName(), PREFIX)) {
+            LOGGER.info("Public headers config have been changed. Event=" + 
event.getType());
+            init();
+          }
+        });
   }
 
   private void init() {
@@ -75,15 +78,20 @@ public class EdgeAddHeaderClientFilter implements 
HttpClientFilter {
 
   @Override
   public void beforeSendRequest(Invocation invocation, HttpServletRequestEx 
requestEx) {
+    addHeaders(invocation, requestEx::addHeader);
+  }
+
+  public void addHeaders(Invocation invocation, BiConsumer<String, String> 
headerAdder) {
     if (!invocation.isEdge()) {
       return;
     }
+    
     HttpServletRequestEx oldRequest = invocation.getRequestEx();
     publicHeaders.forEach(key -> {
-      if (StringUtils.isEmpty(oldRequest.getHeader(key))) {
-        return;
+      String value = oldRequest.getHeader(key);
+      if (StringUtils.isNotEmpty(value)) {
+        headerAdder.accept(key, value);
       }
-      requestEx.addHeader(key, oldRequest.getHeader(key));
     });
   }
 
diff --git 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java
 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java
new file mode 100644
index 0000000..0ea5658
--- /dev/null
+++ 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.edge.core;
+
+import java.util.concurrent.CompletableFuture;
+
+import javax.annotation.Nonnull;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.filter.ConsumerFilter;
+import org.apache.servicecomb.core.filter.FilterNode;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.transport.rest.client.RestClientTransportContext;
+import org.springframework.stereotype.Component;
+
+@Component
+public class EdgeAddHeaderFilter implements ConsumerFilter {
+  public static final String NAME = "edge-add-headers";
+
+  private EdgeAddHeaderClientFilter filter = new EdgeAddHeaderClientFilter();
+
+  @Nonnull
+  @Override
+  public String getName() {
+    return NAME;
+  }
+
+  @Override
+  public boolean isEnabled() {
+    return filter.enabled();
+  }
+
+  @Override
+  public CompletableFuture<Response> onFilter(Invocation invocation, 
FilterNode nextNode) {
+    RestClientTransportContext transportContext = 
invocation.getTransportContext();
+    return CompletableFuture.completedFuture(null)
+        .thenAccept(v -> filter.addHeaders(invocation, 
transportContext.getHttpClientRequest()::putHeader))
+        .thenCompose(v -> nextNode.onFilter(invocation));
+  }
+}
diff --git 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java
 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java
new file mode 100644
index 0000000..17d67a6
--- /dev/null
+++ 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java
@@ -0,0 +1,96 @@
+/*
+ * 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.edge.core;
+
+import static 
org.apache.servicecomb.edge.core.EdgeInvocation.EDGE_INVOCATION_CONTEXT;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.servicecomb.common.rest.RestVertxProducerInvocationCreator;
+import org.apache.servicecomb.common.rest.locator.OperationLocator;
+import org.apache.servicecomb.common.rest.locator.ServicePathManager;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.SCBEngine;
+import org.apache.servicecomb.core.invocation.InvocationFactory;
+import 
org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig;
+import org.apache.servicecomb.core.provider.consumer.ReferenceConfig;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
+
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.RoutingContext;
+
+public class EdgeInvocationCreator extends RestVertxProducerInvocationCreator {
+  protected final String microserviceName;
+
+  protected final String versionRule;
+
+  protected final String path;
+
+  protected MicroserviceReferenceConfig microserviceReferenceConfig;
+
+  public EdgeInvocationCreator(RoutingContext routingContext,
+      HttpServletRequestEx requestEx, HttpServletResponseEx responseEx,
+      String microserviceName, String versionRule, String path) {
+    super(routingContext, null, null, requestEx, responseEx);
+
+    this.microserviceName = microserviceName;
+    this.versionRule = versionRule;
+    this.path = path;
+  }
+
+  @Override
+  public CompletableFuture<Invocation> createAsync() {
+    return createMicroserviceReferenceConfig()
+        .thenCompose(v -> super.createAsync());
+  }
+
+  protected CompletableFuture<Void> createMicroserviceReferenceConfig() {
+    return SCBEngine.getInstance()
+        .createMicroserviceReferenceConfigAsync(microserviceName, versionRule)
+        .thenAccept(mrc -> {
+          this.microserviceReferenceConfig = mrc;
+          this.microserviceMeta = mrc.getLatestMicroserviceMeta();
+        });
+  }
+
+  @Override
+  protected OperationLocator locateOperation(ServicePathManager 
servicePathManager) {
+    return servicePathManager.consumerLocateOperation(path, 
requestEx.getMethod());
+  }
+
+  @Override
+  protected void initInvocationContext(Invocation invocation) {
+    // do not read InvocationContext from HTTP header, for security reason
+  }
+
+  @Override
+  protected Invocation createInstance() {
+    ReferenceConfig referenceConfig = microserviceReferenceConfig
+        .createReferenceConfig(restOperationMeta.getOperationMeta());
+
+    Invocation invocation = InvocationFactory.forConsumer(referenceConfig,
+        restOperationMeta.getOperationMeta(),
+        restOperationMeta.getOperationMeta().buildBaseConsumerRuntimeType(),
+        null);
+    invocation.setSync(false);
+    invocation.setEdge(true);
+    invocation.addLocalContext(EDGE_INVOCATION_CONTEXT, 
Vertx.currentContext());
+
+    return invocation;
+  }
+}
diff --git 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestTransportClient.java
 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestTransportClient.java
index dcd3b97..f8229e0 100644
--- 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestTransportClient.java
+++ 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestTransportClient.java
@@ -32,14 +32,9 @@ public class EdgeRestTransportClient extends 
RestTransportClient {
     Context invocationContext = (Context) 
invocation.getHandlerContext().get(EdgeInvocation.EDGE_INVOCATION_CONTEXT);
 
     URIEndpointObject endpoint = (URIEndpointObject) 
invocation.getEndpoint().getAddress();
-    HttpClientWithContext httpClientWithContext;
-    if (endpoint.isHttp2Enabled()) {
-      httpClientWithContext = HttpClients
-          .getClient(Http2TransportHttpClientOptionsSPI.CLIENT_NAME, 
invocation.isSync(), invocationContext);
-    } else {
-      httpClientWithContext = HttpClients
-          .getClient(HttpTransportHttpClientOptionsSPI.CLIENT_NAME, 
invocation.isSync(), invocationContext);
-    }
-    return httpClientWithContext;
+    String clientName = endpoint.isHttp2Enabled() ?
+        Http2TransportHttpClientOptionsSPI.CLIENT_NAME :
+        HttpTransportHttpClientOptionsSPI.CLIENT_NAME;
+    return HttpClients.getClient(clientName, invocation.isSync(), 
invocationContext);
   }
 }
diff --git 
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.java
 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.java
new file mode 100644
index 0000000..71968af
--- /dev/null
+++ 
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.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.edge.core;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import javax.annotation.Nonnull;
+
+import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.filter.FilterNode;
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.context.TransportContext;
+import org.springframework.stereotype.Component;
+
+@Component
+public class EdgeServerCodecFilter extends RestServerCodecFilter {
+  public static final String NAME = "edge-server-codec";
+
+  @Nonnull
+  @Override
+  public String getName() {
+    return NAME;
+  }
+
+  @Nonnull
+  @Override
+  public List<InvocationType> getInvocationTypes() {
+    return Collections.singletonList(InvocationType.CONSUMER);
+  }
+
+  @Override
+  public CompletableFuture<Response> onFilter(Invocation invocation, 
FilterNode nextNode) {
+    if (invocation.getRequestEx() == null) {
+      // to support normal consumer invocation in edge process
+      return nextNode.onFilter(invocation);
+    }
+
+    return super.onFilter(invocation, nextNode);
+  }
+
+  // save and restore transportContext to support edge invocation
+  @Override
+  protected CompletableFuture<Response> invokeNext(Invocation invocation, 
FilterNode nextNode) {
+    TransportContext transportContext = invocation.getTransportContext();
+    return nextNode.onFilter(invocation)
+        .whenComplete((r, e) -> 
invocation.setTransportContext(transportContext));
+  }
+}
diff --git 
a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestDefaultEdgeDispatcher.java
 
b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestDefaultEdgeDispatcher.java
index e74f13a..639eb02 100644
--- 
a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestDefaultEdgeDispatcher.java
+++ 
b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestDefaultEdgeDispatcher.java
@@ -17,9 +17,10 @@
 
 package org.apache.servicecomb.edge.core;
 
-import java.util.HashMap;
-import java.util.Map;
+import static 
org.apache.servicecomb.edge.core.DefaultEdgeDispatcher.MICROSERVICE_NAME;
+import static org.apache.servicecomb.edge.core.DefaultEdgeDispatcher.VERSION;
 
+import org.apache.servicecomb.core.SCBEngine;
 import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
 import org.junit.After;
 import org.junit.Assert;
@@ -52,21 +53,25 @@ public class TestDefaultEdgeDispatcher {
       , @Mocked RoutingContext context
       , @Mocked HttpServerRequest requst
       , @Mocked EdgeInvocation invocation) {
-    DefaultEdgeDispatcher dispatcher = new DefaultEdgeDispatcher();
-    Map<String, String> pathParams = new HashMap<>();
-    pathParams.put("param0", "testService");
-    pathParams.put("param1", "v1");
+    DefaultEdgeDispatcher dispatcher = new DefaultEdgeDispatcher() {
+      @Override
+      protected boolean isFilterChainEnabled() {
+        return false;
+      }
+    };
 
-    new Expectations() {
+    new Expectations(SCBEngine.class) {
       {
-        router.routeWithRegex("/api/([^\\\\/]+)/([^\\\\/]+)/(.*)");
+        router.routeWithRegex(dispatcher.generateRouteRegex("api", true));
         result = route;
         route.handler((Handler<RoutingContext>) any);
         result = route;
         route.failureHandler((Handler<RoutingContext>) any);
         result = route;
-        context.pathParams();
-        result = pathParams;
+        context.pathParam(MICROSERVICE_NAME);
+        result = "testService";
+        context.pathParam(VERSION);
+        result = "v1";
         context.request();
         result = requst;
         requst.path();
diff --git a/integration-tests/it-edge/src/main/resources/microservice.yaml 
b/integration-tests/it-edge/src/main/resources/microservice.yaml
index 81fad60..a0f10ae 100644
--- a/integration-tests/it-edge/src/main/resources/microservice.yaml
+++ b/integration-tests/it-edge/src/main/resources/microservice.yaml
@@ -19,6 +19,12 @@ service_description:
   name: it-edge
 
 servicecomb:
+  filter-chains:
+    transport:
+      scb-consumer-transport:
+        rest: rest-client-codec, edge-add-headers, rest-client-sender
+    definition:
+      scb-consumer: edge-server-codec, simple-load-balance, 
scb-consumer-transport
   handler:
     chain:
       Consumer:
diff --git 
a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java
 
b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java
index 7f42141..395bdcc 100644
--- 
a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java
+++ 
b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java
@@ -51,15 +51,15 @@ public class Self implements InitializingBean {
   public Self init(AbstractConfiguration configuration) {
     MicroserviceFactory factory = new MicroserviceFactory();
     microservice = factory.create(configuration);
-    microservice.setServiceId(String.format("%s/%s/%s/%s",
+    microservice.serviceId(String.format("%s/%s/%s/%s",
         microservice.getEnvironment(),
         microservice.getAppId(),
         microservice.getServiceName(),
         microservice.getVersion()));
 
-    instance = microservice.getInstance();
-    instance.setInstanceId(UUID.randomUUID().toString());
-    instance.setServiceId(microservice.getServiceId());
+    instance = microservice.getInstance()
+        .instanceId(UUID.randomUUID().toString())
+        .serviceId(microservice.getServiceId());
 
     microserviceInfo
         .setMicroservice(microservice)
diff --git 
a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java
 
b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java
index 716efd3..bb50fbc 100644
--- 
a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java
+++ 
b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java
@@ -16,6 +16,8 @@
  */
 package org.apache.servicecomb.transport.highway;
 
+import java.util.concurrent.CompletableFuture;
+
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager;
@@ -136,7 +138,7 @@ public class HighwayServerConnection extends 
TcpServerConnection implements TcpB
     }
   }
 
-  public Invocation createInvocation(long msgId, RequestHeader header, Buffer 
bodyBuffer) {
+  public CompletableFuture<Invocation> createInvocation(long msgId, 
RequestHeader header, Buffer bodyBuffer) {
     MicroserviceMeta microserviceMeta = 
SCBEngine.getInstance().getProducerMicroserviceMeta();
     SchemaMeta schemaMeta = 
microserviceMeta.ensureFindSchemaMeta(header.getSchemaId());
     OperationMeta operationMeta = 
schemaMeta.ensureFindOperation(header.getOperationName());
@@ -154,6 +156,6 @@ public class HighwayServerConnection extends 
TcpServerConnection implements TcpB
         
.setOperationProtobuf(ProtobufManager.getOrCreateOperation(invocation));
     invocation.setTransportContext(transportContext);
 
-    return invocation;
+    return CompletableFuture.completedFuture(invocation);
   }
 }
diff --git 
a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
 
b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
index 0a8e581..9c1885c 100644
--- 
a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
+++ 
b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
@@ -32,9 +32,9 @@ import org.apache.servicecomb.core.event.ServerAccessLogEvent;
 import org.apache.servicecomb.core.transport.AbstractTransport;
 import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.apache.servicecomb.foundation.common.utils.BeanUtils;
 import org.apache.servicecomb.foundation.common.utils.ExceptionUtils;
 import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
-
 import org.apache.servicecomb.foundation.ssl.SSLCustom;
 import org.apache.servicecomb.foundation.ssl.SSLOption;
 import org.apache.servicecomb.foundation.ssl.SSLOptionFactory;
@@ -219,7 +219,9 @@ public class RestServerVerticle extends AbstractVerticle {
   }
 
   private void initDispatcher(Router mainRouter) {
-    List<VertxHttpDispatcher> dispatchers = 
SPIServiceUtils.getSortedService(VertxHttpDispatcher.class);
+    List<VertxHttpDispatcher> dispatchers = 
SPIServiceUtils.loadSortedService(VertxHttpDispatcher.class);
+    BeanUtils.addBeans(VertxHttpDispatcher.class, dispatchers);
+    
     for (VertxHttpDispatcher dispatcher : dispatchers) {
       if (dispatcher.enabled()) {
         dispatcher.init(mainRouter);
diff --git 
a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.java
 
b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.java
index cc8b6dc..cb96d8c 100644
--- 
a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.java
+++ 
b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.java
@@ -17,11 +17,13 @@
 
 package org.apache.servicecomb.transport.rest.vertx;
 
-import io.vertx.ext.web.Router;
+import org.apache.servicecomb.foundation.common.utils.SPIEnabled;
+import org.apache.servicecomb.foundation.common.utils.SPIOrder;
 
-public interface VertxHttpDispatcher {
-  int getOrder();
+import io.vertx.ext.web.Router;
 
+public interface VertxHttpDispatcher extends SPIOrder, SPIEnabled {
+  @Override
   default boolean enabled() {
     return true;
   }

Reply via email to