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 25631cd1e [SCB-2743]edge service support retry governance (#4038)
25631cd1e is described below

commit 25631cd1e52f8e5e217617cbfdf0501d36fdb7ff
Author: liubao68 <[email protected]>
AuthorDate: Fri Nov 17 11:01:02 2023 +0800

    [SCB-2743]edge service support retry governance (#4038)
---
 .../core/filter/CoreFilterConfiguration.java       |   7 +
 .../servicecomb/core/filter/impl/RetryFilter.java  | 135 ++++++++++++++
 .../servicecomb/core/governance/RetryContext.java  |   1 +
 .../ServiceCombCircuitBreakerExtension.java        |  13 +-
 .../ServiceCombInstanceIsolationExtension.java     |   8 +-
 .../core/governance/ServiceCombRetryExtension.java |  13 +-
 .../core/provider/consumer/InvokerUtils.java       | 196 +--------------------
 .../core/provider/consumer/TestInvokerUtils.java   |   7 +-
 .../demo/filter/client/RetryClientSchema.java      |  25 +++
 .../src/main/resources/microservice.yaml           |   8 +
 .../demo/filter/tests/TestRetrySchemaFromEdge.java |  13 ++
 .../src/main/resources/microservice.yaml           |  10 --
 .../filterEdge/RetryClientSchema.yaml              |  23 ++-
 .../src/main/resources/microservice.yaml           |  10 +-
 .../governance/handler/CircuitBreakerHandler.java  |   2 +-
 .../handler/InstanceIsolationHandler.java          |   2 +-
 .../governance/handler/RetryHandler.java           |   2 +-
 .../governance/handler/ext/FailurePredictor.java   |   4 +-
 .../governance/AbstractFailurePredictorTest.java   |   4 +-
 .../servicecomb/governance/MockRetryExtension.java |   2 +-
 .../servicecomb/governance/RetryHandlerTest.java   |   6 +-
 21 files changed, 250 insertions(+), 241 deletions(-)

diff --git 
a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java
 
b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java
index 7359fdc55..d5a828a18 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java
@@ -18,7 +18,9 @@ package org.apache.servicecomb.core.filter;
 
 import org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter;
 import org.apache.servicecomb.core.filter.impl.ProviderOperationFilter;
+import org.apache.servicecomb.core.filter.impl.RetryFilter;
 import org.apache.servicecomb.core.filter.impl.ScheduleFilter;
+import org.apache.servicecomb.governance.handler.RetryHandler;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -34,6 +36,11 @@ public class CoreFilterConfiguration {
     return new ScheduleFilter();
   }
 
+  @Bean
+  public RetryFilter retryFilter(RetryHandler retryHandler) {
+    return new RetryFilter(retryHandler);
+  }
+
   @Bean
   public FilterChainsManager filterChainsManager() {
     return new FilterChainsManager();
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java 
b/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java
new file mode 100644
index 000000000..e423b8f7e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java
@@ -0,0 +1,135 @@
+/*
+ * 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.core.filter.impl;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.filter.AbstractFilter;
+import org.apache.servicecomb.core.filter.ConsumerFilter;
+import org.apache.servicecomb.core.filter.EdgeFilter;
+import org.apache.servicecomb.core.filter.Filter;
+import org.apache.servicecomb.core.filter.FilterNode;
+import org.apache.servicecomb.core.governance.GovernanceConfiguration;
+import org.apache.servicecomb.core.governance.MatchType;
+import org.apache.servicecomb.core.governance.RetryContext;
+import org.apache.servicecomb.governance.handler.RetryHandler;
+import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import io.github.resilience4j.decorators.Decorators;
+import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage;
+import io.github.resilience4j.retry.Retry;
+
+public class RetryFilter extends AbstractFilter implements ConsumerFilter, 
EdgeFilter {
+  private static final Object LOCK = new Object();
+
+  private static volatile ScheduledExecutorService reactiveRetryPool;
+
+  private static ScheduledExecutorService getOrCreateRetryPool() {
+    if (reactiveRetryPool == null) {
+      synchronized (LOCK) {
+        if (reactiveRetryPool == null) {
+          reactiveRetryPool = Executors.newScheduledThreadPool(2, new 
ThreadFactory() {
+            private final AtomicInteger count = new AtomicInteger(0);
+
+            @Override
+            public Thread newThread(Runnable r) {
+              Thread thread = new Thread(r, "reactive-retry-pool-thread-" + 
count.getAndIncrement());
+              // avoid block shutdown
+              thread.setDaemon(true);
+              return thread;
+            }
+          });
+        }
+      }
+    }
+    return reactiveRetryPool;
+  }
+
+  private final RetryHandler retryHandler;
+
+  @Autowired
+  public RetryFilter(RetryHandler retryHandler) {
+    this.retryHandler = retryHandler;
+  }
+
+  @Override
+  public String getName() {
+    return "retry";
+  }
+
+  @Override
+  public CompletableFuture<Response> onFilter(Invocation invocation, 
FilterNode nextNode) {
+    GovernanceRequestExtractor request = 
MatchType.createGovHttpRequest(invocation);
+    Retry retry = retryHandler.getActuator(request);
+    if (retry == null) {
+      return nextNode.onFilter(invocation);
+    }
+
+    Supplier<CompletionStage<Response>> next = 
createBusinessCompletionStageSupplier(invocation, nextNode);
+    DecorateCompletionStage<Response> dcs = Decorators.ofCompletionStage(next);
+    dcs.withRetry(retry, getOrCreateRetryPool());
+    CompletableFuture<Response> future = new CompletableFuture<>();
+    dcs.get().whenComplete((r, e) -> {
+      if (e == null) {
+        future.complete(r);
+        return;
+      }
+
+      future.completeExceptionally(e);
+    });
+
+    return future;
+  }
+
+  private Supplier<CompletionStage<Response>> 
createBusinessCompletionStageSupplier(Invocation invocation,
+      FilterNode nextNode) {
+    return () -> {
+      updateRetryStatus(invocation);
+      return nextNode.onFilter(invocation);
+    };
+  }
+
+  private static void updateRetryStatus(Invocation invocation) {
+    if (invocation.getLocalContext(RetryContext.RETRY_CONTEXT) != null) {
+      if (invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE) != null
+          && (boolean) 
invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE)) {
+        // clear last server to avoid using user defined endpoint
+        invocation.setEndpoint(null);
+      }
+      RetryContext retryContext = 
invocation.getLocalContext(RetryContext.RETRY_CONTEXT);
+      retryContext.incrementRetry();
+      return;
+    }
+
+    invocation.addLocalContext(RetryContext.RETRY_CONTEXT,
+        new 
RetryContext(GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName())));
+  }
+
+  @Override
+  public int getOrder() {
+    return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java 
b/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java
index 058982a42..47792792e 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java
@@ -19,6 +19,7 @@ package org.apache.servicecomb.core.governance;
 public class RetryContext {
   public static final String RETRY_CONTEXT = "x-context-retry";
 
+  // weather need reset Endpoint in retry
   public static final String RETRY_LOAD_BALANCE = 
"x-context-retry-loadbalance";
 
   private boolean retry;
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java
 
b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java
index cb85ecfe1..9177ca718 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java
@@ -17,11 +17,10 @@
 
 package org.apache.servicecomb.core.governance;
 
-import jakarta.ws.rs.core.Response.Status;
+import java.util.List;
 
 import 
org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension;
 import org.apache.servicecomb.swagger.invocation.Response;
-import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
 
 public class ServiceCombCircuitBreakerExtension extends 
AbstractCircuitBreakerExtension {
@@ -41,15 +40,11 @@ public class ServiceCombCircuitBreakerExtension extends 
AbstractCircuitBreakerEx
   }
 
   @Override
-  public boolean isFailedResult(Throwable e) {
+  public boolean isFailedResult(List<String> statusList, Throwable e) {
     if (e instanceof InvocationException) {
       InvocationException invocationException = (InvocationException) e;
-      if (invocationException.getStatusCode() == 
Status.SERVICE_UNAVAILABLE.getStatusCode() ||
-          invocationException.getStatusCode() == 
Status.BAD_GATEWAY.getStatusCode() ||
-          invocationException.getStatusCode() == 
ExceptionFactory.PRODUCER_INNER_STATUS_CODE) {
-        return true;
-      }
+      return 
statusList.contains(String.valueOf(invocationException.getStatusCode()));
     }
-    return super.isFailedResult(e);
+    return super.isFailedResult(statusList, e);
   }
 }
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java
 
b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java
index f3b9075ea..2bbf6d3c8 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java
@@ -17,10 +17,12 @@
 
 package org.apache.servicecomb.core.governance;
 
+import java.util.List;
+
 import 
org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension;
 
 public class ServiceCombInstanceIsolationExtension extends 
AbstractInstanceIsolationExtension {
-  private ServiceCombRetryExtension retryExtension = new 
ServiceCombRetryExtension();
+  private final ServiceCombRetryExtension retryExtension = new 
ServiceCombRetryExtension();
 
   @Override
   protected String extractStatusCode(Object result) {
@@ -28,7 +30,7 @@ public class ServiceCombInstanceIsolationExtension extends 
AbstractInstanceIsola
   }
 
   @Override
-  public boolean isFailedResult(Throwable e) {
-    return retryExtension.isFailedResult(e);
+  public boolean isFailedResult(List<String> statusList, Throwable e) {
+    return retryExtension.isFailedResult(statusList, e);
   }
 }
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java
 
b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java
index ccd7f6f84..871a03bfa 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java
@@ -17,11 +17,10 @@
 
 package org.apache.servicecomb.core.governance;
 
-import jakarta.ws.rs.core.Response.Status;
+import java.util.List;
 
 import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension;
 import org.apache.servicecomb.swagger.invocation.Response;
-import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
 
 public class ServiceCombRetryExtension extends AbstractRetryExtension {
@@ -41,15 +40,11 @@ public class ServiceCombRetryExtension extends 
AbstractRetryExtension {
   }
 
   @Override
-  public boolean isFailedResult(Throwable e) {
+  public boolean isFailedResult(List<String> statusList, Throwable e) {
     if (e instanceof InvocationException) {
       InvocationException invocationException = (InvocationException) e;
-      if (invocationException.getStatusCode() == 
Status.SERVICE_UNAVAILABLE.getStatusCode() ||
-          invocationException.getStatusCode() == 
Status.BAD_GATEWAY.getStatusCode() ||
-          invocationException.getStatusCode() == 
ExceptionFactory.CONSUMER_INNER_STATUS_CODE) {
-        return true;
-      }
+      return 
statusList.contains(String.valueOf(invocationException.getStatusCode()));
     }
-    return super.isFailedResult(e);
+    return super.isFailedResult(statusList, e);
   }
 }
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java
 
b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java
index 5c2a012ae..d850cf6b6 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java
@@ -18,22 +18,14 @@
 package org.apache.servicecomb.core.provider.consumer;
 
 import static 
org.apache.servicecomb.core.exception.Exceptions.toConsumerResponse;
-import static 
org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory.CONSUMER_INNER_STATUS_CODE;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
-import java.time.Duration;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Supplier;
 
 import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.core.SCBEngine;
@@ -41,62 +33,19 @@ import 
org.apache.servicecomb.core.definition.InvocationRuntimeType;
 import org.apache.servicecomb.core.definition.MicroserviceMeta;
 import org.apache.servicecomb.core.definition.OperationMeta;
 import org.apache.servicecomb.core.definition.SchemaMeta;
-import org.apache.servicecomb.core.governance.GovernanceConfiguration;
-import org.apache.servicecomb.core.governance.MatchType;
-import org.apache.servicecomb.core.governance.RetryContext;
 import org.apache.servicecomb.core.invocation.InvocationFactory;
 import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
-import org.apache.servicecomb.foundation.common.utils.BeanUtils;
-import org.apache.servicecomb.governance.handler.RetryHandler;
-import org.apache.servicecomb.governance.handler.ext.FailurePredictor;
-import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor;
 import org.apache.servicecomb.swagger.invocation.AsyncResponse;
 import org.apache.servicecomb.swagger.invocation.Response;
 import org.apache.servicecomb.swagger.invocation.context.ContextUtils;
 import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
 import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import com.google.common.annotations.VisibleForTesting;
-
-import io.github.resilience4j.decorators.Decorators;
-import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage;
-import io.github.resilience4j.retry.Retry;
-import io.github.resilience4j.retry.RetryConfig;
-import io.github.resilience4j.retry.RetryRegistry;
 import io.vertx.core.Context;
 import jakarta.ws.rs.core.Response.Status;
 
 public final class InvokerUtils {
-  private static final Logger LOGGER = 
LoggerFactory.getLogger(InvokerUtils.class);
-
-  private static final Object LOCK = new Object();
-
-  private static volatile ScheduledExecutorService reactiveRetryPool;
-
-  private static ScheduledExecutorService getOrCreateRetryPool() {
-    if (reactiveRetryPool == null) {
-      synchronized (LOCK) {
-        if (reactiveRetryPool == null) {
-          reactiveRetryPool = Executors.newScheduledThreadPool(2, new 
ThreadFactory() {
-            private final AtomicInteger count = new AtomicInteger(0);
-
-            @Override
-            public Thread newThread(Runnable r) {
-              Thread thread = new Thread(r, "reactive-retry-pool-thread-" + 
count.getAndIncrement());
-              // avoid block shutdown
-              thread.setDaemon(true);
-              return thread;
-            }
-          });
-        }
-      }
-    }
-    return reactiveRetryPool;
-  }
-
   @SuppressWarnings({"unchecked"})
   public static <T> T syncInvoke(String microserviceName, String transport,
       String schemaId, String operationId, Map<String, Object> 
swaggerArguments, Type responseType) {
@@ -191,43 +140,6 @@ public final class InvokerUtils {
     return toSync(invoke(invocation), invocation.getWaitTime());
   }
 
-  private static void updateRetryStatus(Invocation invocation) {
-    if (invocation.isFinished()) {
-      invocation.reset();
-      if (invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE) != null
-          && (boolean) 
invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE)) {
-        // clear last server to avoid using user defined endpoint
-        invocation.setEndpoint(null);
-      }
-      RetryContext retryContext = 
invocation.getLocalContext(RetryContext.RETRY_CONTEXT);
-      retryContext.incrementRetry();
-      return;
-    }
-
-    invocation.addLocalContext(RetryContext.RETRY_CONTEXT,
-        new 
RetryContext(GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName())));
-  }
-
-  private static boolean isCompatibleRetryEnabled(Invocation invocation) {
-    // maxAttempts must be greater than or equal to 1
-    return 
GovernanceConfiguration.isRetryEnabled(invocation.getMicroserviceName())
-        && 
GovernanceConfiguration.getRetryNextServer(invocation.getMicroserviceName())
-        + 
GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()) > 
0;
-  }
-
-  private static Retry getOrCreateCompatibleRetry(Invocation invocation) {
-    RetryConfig retryConfig = RetryConfig.custom()
-        // max attempts include the first call
-        
.maxAttempts(GovernanceConfiguration.getRetryNextServer(invocation.getMicroserviceName())
-            + 
GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()) + 
1)
-        .retryOnResult(InvokerUtils::canRetryForStatusCode)
-        .retryOnException(InvokerUtils::canRetryForException)
-        .waitDuration(Duration.ofMillis(1))
-        .build();
-    RetryRegistry retryRegistry = RetryRegistry.of(retryConfig);
-    return retryRegistry.retry(invocation.getMicroserviceName());
-  }
-
   /**
    * This is an internal API, caller make sure already invoked 
SCBEngine.ensureStatusUp
    */
@@ -241,22 +153,6 @@ public final class InvokerUtils {
     });
   }
 
-  private static void decorateReactiveRetry(Invocation invocation, 
DecorateCompletionStage<Response> dcs,
-      GovernanceRequestExtractor request) {
-    // governance implementations.
-    RetryHandler retryHandler = BeanUtils.getBean(RetryHandler.class);
-    Retry retry = retryHandler.getActuator(request);
-    if (retry != null) {
-      dcs.withRetry(retry, getOrCreateRetryPool());
-    }
-
-    if (isCompatibleRetryEnabled(invocation)) {
-      // compatible implementation for retry in load balance module in old 
versions.
-      retry = getOrCreateCompatibleRetry(invocation);
-      dcs.withRetry(retry, getOrCreateRetryPool());
-    }
-  }
-
   public static boolean isSyncMethod(Method method) {
     return !isAsyncMethod(method);
   }
@@ -287,89 +183,13 @@ public final class InvokerUtils {
    * NOTE: this method should never throw exception directly
    */
   public static CompletableFuture<Response> invoke(Invocation invocation) {
-    Supplier<CompletionStage<Response>> next = invokeImpl(invocation);
-    DecorateCompletionStage<Response> dcs = Decorators.ofCompletionStage(next);
-    GovernanceRequestExtractor request = 
MatchType.createGovHttpRequest(invocation);
-
-    decorateReactiveRetry(invocation, dcs, request);
-
-    CompletableFuture<Response> result = new CompletableFuture<>();
-    dcs.get().whenComplete((r, e) -> {
-      ContextUtils.setInvocationContext(invocation.getParentContext());
-
-      if (e == null) {
-        result.complete(r);
-        return;
-      }
-
-      String message = String.format("invoke failed, operation %s, trace id 
%s",
-          invocation.getMicroserviceQualifiedName(),
-          invocation.getTraceId());
-      LOGGER.error(message, e);
-      Response response = Response.createConsumerFail(e, message);
-      invocation.onFinish(response);
-      result.complete(response);
-    });
-    return result;
-  }
-
-  private static Supplier<CompletionStage<Response>> invokeImpl(Invocation 
invocation) {
-    return () -> {
-      invocation.onStart(null);
-      updateRetryStatus(invocation);
-      if (invocation.isEdge()) {
-        return invocation.getMicroserviceMeta().getEdgeFilterChain()
-            .onFilter(invocation)
-            .exceptionally(throwable -> toConsumerResponse(invocation, 
throwable))
-            .whenComplete((response, throwable) -> 
finishInvocation(invocation, response));
-      }
-      return invocation.getMicroserviceMeta().getConsumerFilterChain()
-          .onFilter(invocation)
-          .exceptionally(throwable -> toConsumerResponse(invocation, 
throwable))
-          .whenComplete((response, throwable) -> finishInvocation(invocation, 
response));
-    };
-  }
-
-  private static void finishInvocation(Invocation invocation, Response ar) {
-    invocation.onFinish(ar);
-
-    if (ar.isFailed()) {
-      // re-throw exception to make sure retry based on exception
-      // for InvocationException, users can configure status code for retry
-      // for 490, details error are wrapped, need re-throw
-
-      if (!(ar.getResult() instanceof InvocationException)) {
-        throw AsyncUtils.rethrow(ar.getResult());
-      }
-
-      if (((InvocationException) ar.getResult()).getStatusCode() == 
CONSUMER_INNER_STATUS_CODE) {
-        throw AsyncUtils.rethrow(ar.getResult());
-      }
-    }
-  }
-
-  @VisibleForTesting
-  static boolean canRetryForException(Throwable e) {
-    if (e instanceof InvocationException && ((InvocationException) 
e).getStatusCode() == Status.SERVICE_UNAVAILABLE
-        .getStatusCode()) {
-      return true;
-    }
-    return 
FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, e);
-  }
-
-  @VisibleForTesting
-  static boolean canRetryForStatusCode(Object response) {
-    // retry on status code 503
-    if (!(response instanceof Response resp)) {
-      return false;
-    }
-    if (!resp.isFailed()) {
-      return false;
-    }
-    if (resp.getResult() instanceof InvocationException) {
-      InvocationException e = resp.getResult();
-      return e.getStatusCode() == 503;
-    }
-    return false;
+    invocation.onStart(null);
+    return invocation.getMicroserviceMeta().getConsumerFilterChain()
+        .onFilter(invocation)
+        .exceptionally(throwable -> toConsumerResponse(invocation, throwable))
+        .whenComplete((response, throwable) -> {
+          ContextUtils.setInvocationContext(invocation.getParentContext());
+          invocation.onFinish(response);
+        });
   }
 }
diff --git 
a/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java
 
b/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java
index d526219c5..3b3e56092 100644
--- 
a/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java
+++ 
b/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java
@@ -17,16 +17,11 @@
 
 package org.apache.servicecomb.core.provider.consumer;
 
-import org.apache.servicecomb.swagger.invocation.Response;
-import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 public class TestInvokerUtils {
   @Test
   public void testRetryInvocation503() {
-    InvocationException root = new InvocationException(503, "Service 
Unavailable", "Error");
-    boolean canRetry = 
InvokerUtils.canRetryForStatusCode(Response.failResp(root));
-    Assertions.assertTrue(canRetry);
+
   }
 }
diff --git 
a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java
 
b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java
index e101db5a8..b5951557a 100644
--- 
a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java
+++ 
b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java
@@ -17,15 +17,19 @@
 package org.apache.servicecomb.demo.filter.client;
 
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.servicecomb.provider.pojo.RpcReference;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
 import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.client.RestOperations;
 
+import jakarta.ws.rs.core.Response.Status;
+
 @RestSchema(schemaId = "RetryClientSchema")
 @RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE)
 public class RetryClientSchema {
@@ -38,6 +42,27 @@ public class RetryClientSchema {
 
   RestOperations restTemplate = RestTemplateBuilder.create();
 
+  private AtomicLong counter = new AtomicLong(0);
+
+  @GetMapping(path = "/governance/edgeSuccessWhenRetry")
+  public boolean edgeSuccessWhenRetry() {
+    if (counter.getAndIncrement() % 3 != 0) {
+      throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again 
later.");
+    }
+    return true;
+  }
+
+  @GetMapping(path = "/governance/edgeSuccessWhenRetryAsync")
+  public CompletableFuture<Boolean> edgeSuccessWhenRetryAsync() {
+    CompletableFuture<Boolean> result = new CompletableFuture<>();
+    if (counter.getAndIncrement() % 2 == 0) {
+      result.completeExceptionally(new 
InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."));
+    } else {
+      result.complete(true);
+    }
+    return result;
+  }
+
   @GetMapping(path = "/governance/successWhenRetry")
   public boolean successWhenRetry() {
     return 
restTemplate.getForObject("servicecomb://filterServer/retry/governance/successWhenRetry",
diff --git a/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml 
b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml
index b70747606..034b8cd04 100644
--- a/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml
+++ b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml
@@ -58,8 +58,16 @@ servicecomb:
       matches:
         - apiPath:
             exact: "/govern/edgeFlowControl"
+    retry-governance: |
+      matches:
+        - apiPath:
+            prefix: "/retry/governance/"
   rateLimiting:
     edgeFlowControl: |
       timeoutDuration: 0
       limitRefreshPeriod: 1000
       rate: 1
+  retry:
+    retry-governance: |
+      maxAttempts: 2
+      retryOnResponseStatus: [500]
diff --git 
a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java
 
b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java
index 422297296..d637a8b2a 100644
--- 
a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java
+++ 
b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java
@@ -29,8 +29,12 @@ import org.springframework.web.client.RestTemplate;
 @Component
 public class TestRetrySchemaFromEdge implements CategorizedTestCase {
   interface RetrySchemaInf {
+    boolean edgeSuccessWhenRetry();
+
     boolean successWhenRetry();
 
+    CompletableFuture<Boolean> edgeSuccessWhenRetryAsync();
+
     CompletableFuture<Boolean> successWhenRetryAsync();
   }
 
@@ -55,6 +59,7 @@ public class TestRetrySchemaFromEdge implements 
CategorizedTestCase {
     testRetryGovernanceFromEdgeDefaultDispatcher();
     testRetryGovernanceRestTemplate();
     testRetryGovernanceRpc();
+    testEdgeRetryGovernanceRpc();
   }
 
   private void testRetryGovernanceRpc() throws Exception {
@@ -65,6 +70,14 @@ public class TestRetrySchemaFromEdge implements 
CategorizedTestCase {
     TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true);
   }
 
+  private void testEdgeRetryGovernanceRpc() throws Exception {
+    TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true);
+    TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true);
+
+    TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true);
+    TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true);
+  }
+
   private void testRetryGovernanceRestTemplate() {
     TestMgr.check(restTemplate.getForObject(
         SERVER + "/retry/governance/successWhenRetry", boolean.class), true);
diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml 
b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml
index 75a286db4..c6b2ea0fd 100644
--- a/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml
+++ b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml
@@ -28,13 +28,3 @@ servicecomb:
   invocation:
     exception:
       print-stack-trace: true
-  # test governance retry
-  matchGroup:
-    retry-governance: |
-      matches:
-        - apiPath:
-            prefix: "/retry/governance/"
-  retry:
-    retry-governance: |
-      maxAttempts: 2
-      retryOnResponseStatus: [500]
diff --git 
a/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml
 
b/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml
index 3b8376002..77517becf 100644
--- 
a/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml
+++ 
b/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml
@@ -22,6 +22,26 @@ info:
 servers:
 - url: /retry
 paths:
+  /governance/edgeSuccessWhenRetry:
+    get:
+      operationId: edgeSuccessWhenRetry
+      responses:
+        "200":
+          description: response of 200
+          content:
+            application/json:
+              schema:
+                type: boolean
+  /governance/edgeSuccessWhenRetryAsync:
+    get:
+      operationId: edgeSuccessWhenRetryAsync
+      responses:
+        "200":
+          description: response of 200
+          content:
+            application/json:
+              schema:
+                type: boolean
   /governance/successWhenRetry:
     get:
       operationId: successWhenRetry
@@ -42,5 +62,4 @@ paths:
             application/json:
               schema:
                 type: boolean
-components:
-  schemas: {}
\ No newline at end of file
+components: {}
diff --git 
a/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml 
b/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml
index eec6b301c..2fe0f881f 100644
--- a/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml
+++ b/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml
@@ -54,9 +54,6 @@ servicecomb:
     userDefinedEndpoint.enabled: true
     strategy:
       name: WeightedResponse
-    retryEnabled: true
-    retryOnSame: 1
-    retryOnNext: 1
     filter.status.enabled: false
   fallbackpolicy:
     Consumer:
@@ -113,10 +110,17 @@ servicecomb:
       matches:
         - apiPath:
             prefix: "/retry/governance/"
+    retry-success: |
+      matches:
+        - apiPath:
+            prefix: "/codeFirstSpringmvc/retrySuccess"
   retry:
     retry-governance: |
       maxAttempts: 2
       retryOnResponseStatus: [500]
+    retry-success: |
+      maxAttempts: 2
+      retryOnResponseStatus: [503]
 
 #########SSL options
 # open jdk 8 now TLSv1.3 not available
diff --git 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java
 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java
index fdeb10a5a..39082661b 100644
--- 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java
+++ 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java
@@ -72,7 +72,7 @@ public class CircuitBreakerHandler extends 
AbstractGovernanceHandler<CircuitBrea
         .minimumNumberOfCalls(policy.getMinimumNumberOfCalls())
         .slidingWindowType(policy.getSlidingWindowTypeEnum())
         .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize()))
-        .recordException(circuitBreakerExtension::isFailedResult)
+        .recordException(e -> 
circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), e))
         .recordResult(r -> 
circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), r))
         .build();
     CircuitBreakerRegistry circuitBreakerRegistry = 
CircuitBreakerRegistry.of(circuitBreakerConfig);
diff --git 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java
 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java
index 5be1978dd..b37967b5a 100644
--- 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java
+++ 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java
@@ -103,7 +103,7 @@ public class InstanceIsolationHandler extends 
AbstractGovernanceHandler<CircuitB
         .minimumNumberOfCalls(policy.getMinimumNumberOfCalls())
         .slidingWindowType(policy.getSlidingWindowTypeEnum())
         .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize()))
-        .recordException(isolationExtension::isFailedResult)
+        .recordException(e -> 
isolationExtension.isFailedResult(policy.getRecordFailureStatus(), e))
         .recordResult(r -> 
isolationExtension.isFailedResult(policy.getRecordFailureStatus(), r))
         .build();
     CircuitBreakerRegistry circuitBreakerRegistry = 
CircuitBreakerRegistry.of(circuitBreakerConfig);
diff --git 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java
 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java
index 8b4c819b0..f7f2476f6 100644
--- 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java
+++ 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java
@@ -68,7 +68,7 @@ public class RetryHandler extends 
AbstractGovernanceHandler<Retry, RetryPolicy>
     RetryConfig config = RetryConfig.custom()
         .maxAttempts(retryPolicy.getMaxAttempts() + 1)
         .retryOnResult(response -> 
retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), response))
-        .retryOnException(retryExtension::isFailedResult)
+        .retryOnException(exception -> 
retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), 
exception))
         .intervalFunction(getIntervalFunction(retryPolicy))
         .failAfterMaxAttempts(retryPolicy.isFailAfterMaxAttempts())
         .build();
diff --git 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java
 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java
index 31ae8eca1..c07ebe39b 100644
--- 
a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java
+++ 
b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java
@@ -29,8 +29,8 @@ import javax.net.ssl.SSLHandshakeException;
 
 import com.google.common.collect.ImmutableMap;
 
-import io.vertx.core.VertxException;
 import io.netty.handler.ssl.SslHandshakeTimeoutException;
+import io.vertx.core.VertxException;
 
 public interface FailurePredictor {
   Map<Class<? extends Throwable>, List<String>> STRICT_RETRIABLE =
@@ -49,7 +49,7 @@ public interface FailurePredictor {
 
   boolean isFailedResult(List<String> statusList, Object result);
 
-  default boolean isFailedResult(Throwable e) {
+  default boolean isFailedResult(List<String> statusList, Throwable e) {
     return canRetryForException(STRICT_RETRIABLE, e);
   }
 
diff --git 
a/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java
 
b/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java
index 9c84287ac..dedeba800 100644
--- 
a/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java
+++ 
b/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java
@@ -35,8 +35,8 @@ public class AbstractFailurePredictorTest {
     }
 
     @Override
-    public boolean isFailedResult(Throwable e) {
-      return super.isFailedResult(e);
+    public boolean isFailedResult(List<String> statusList, Throwable e) {
+      return super.isFailedResult(statusList, e);
     }
   }
 
diff --git 
a/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java
 
b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java
index 1dd272ca1..2badc9c15 100644
--- 
a/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java
+++ 
b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java
@@ -34,7 +34,7 @@ public class MockRetryExtension extends 
AbstractRetryExtension {
   }
 
   @Override
-  public boolean isFailedResult(Throwable e) {
+  public boolean isFailedResult(List<String> statusList, Throwable e) {
     return false;
   }
 }
diff --git 
a/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java
 
b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java
index 1223bfe74..073cfc445 100644
--- 
a/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java
+++ 
b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java
@@ -35,7 +35,7 @@ public class RetryHandlerTest {
     AbstractRetryExtension retryExtension = 
Mockito.mock(AbstractRetryExtension.class);
     RetryProperties retryProperties = Mockito.mock(RetryProperties.class);
     GovernanceRequest governanceRequest = 
Mockito.mock(GovernanceRequest.class);
-    
Mockito.when(retryExtension.isFailedResult(Mockito.any())).thenReturn(true);
+    Mockito.when(retryExtension.isFailedResult(Mockito.any(), 
Mockito.any())).thenReturn(true);
 
     RetryPolicy retryPolicy = new RetryPolicy();
     retryPolicy.setName("test");
@@ -53,7 +53,7 @@ public class RetryHandlerTest {
     AbstractRetryExtension retryExtension = 
Mockito.mock(AbstractRetryExtension.class);
     RetryProperties retryProperties = Mockito.mock(RetryProperties.class);
     GovernanceRequest governanceRequest = 
Mockito.mock(GovernanceRequest.class);
-    
Mockito.when(retryExtension.isFailedResult(Mockito.any())).thenReturn(true);
+    Mockito.when(retryExtension.isFailedResult(Mockito.any(), 
Mockito.any())).thenReturn(true);
 
     RetryPolicy retryPolicy = new RetryPolicy();
     retryPolicy.setName("test");
@@ -71,7 +71,7 @@ public class RetryHandlerTest {
     AbstractRetryExtension retryExtension = 
Mockito.mock(AbstractRetryExtension.class);
     RetryProperties retryProperties = Mockito.mock(RetryProperties.class);
     GovernanceRequest governanceRequest = 
Mockito.mock(GovernanceRequest.class);
-    Mockito.when(retryExtension.isFailedResult(Mockito.any(), 
Mockito.any())).thenReturn(true);
+    Mockito.when(retryExtension.isFailedResult(Mockito.any(), (Object) 
Mockito.any())).thenReturn(true);
 
     RetryPolicy retryPolicy = new RetryPolicy();
     retryPolicy.setName("test");


Reply via email to