diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/exception/ErrorPolicy.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/exception/ErrorPolicy.java index 7807cd305..c1118c170 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/exception/ErrorPolicy.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/exception/ErrorPolicy.java @@ -15,7 +15,7 @@ */ package com.jd.live.agent.governance.exception; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; +import com.jd.live.agent.governance.policy.service.exception.ErrorParserPolicy; import java.util.Set; @@ -27,9 +27,9 @@ public interface ErrorPolicy { /** * Returns the code policy associated with this component. * - * @return the code policy + * @return the error code parse policy */ - CodePolicy getCodePolicy(); + ErrorParserPolicy getCodePolicy(); /** * Returns a set of error codes that are considered critical by this component. @@ -38,6 +38,20 @@ public interface ErrorPolicy { */ Set getErrorCodes(); + /** + * Returns the error message policy associated with this component. + * + * @return the error message parse policy + */ + ErrorParserPolicy getMessagePolicy(); + + /** + * Returns a set of error messages that are considered critical by this component. + * + * @return a set of critical error messages + */ + Set getErrorMessages(); + /** * Returns a set of exceptions that are considered critical by this component. * @@ -67,6 +81,16 @@ public interface ErrorPolicy { * @return true if the error message is found, false otherwise */ default boolean containsErrorMessage(String errorMessage) { + if (errorMessage != null && !errorMessage.isEmpty()) { + Set messages = getErrorMessages(); + if (messages != null && !messages.isEmpty()) { + for (String message : messages) { + if (message.contains(errorMessage)) { + return true; + } + } + } + } return false; } @@ -86,6 +110,33 @@ default boolean containsErrorMessage(String errorMessage) { */ boolean containsException(Set classNames); + /** + * Checks if the response body is required for error parsing. + * + * @return true if the response body is required for error parsing, false otherwise + * @see ErrorParserPolicy#requireResponseBody() + */ + default boolean requireResponseBody() { + ErrorParserPolicy codePolicy = getCodePolicy(); + ErrorParserPolicy messagePolicy = getMessagePolicy(); + return codePolicy != null && codePolicy.requireResponseBody() || messagePolicy != null && messagePolicy.requireResponseBody(); + } + + /** + * Checks if the given status code and content type match the configured values. + * + * @param status the status code to check + * @param contentType the content type to check + * @param okStatus the OK status code to consider as a match + * @return true if the status code and content type match, false otherwise + */ + default boolean match(Integer status, String contentType, Integer okStatus) { + ErrorParserPolicy codePolicy = getCodePolicy(); + ErrorParserPolicy messagePolicy = getMessagePolicy(); + return codePolicy != null && codePolicy.match(status, contentType, okStatus) + || messagePolicy != null && messagePolicy.match(status, contentType, okStatus); + } + /** * Checks if any of the exception sources are present in the set of target exceptions. * diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/cluster/FailoverClusterInvoker.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/cluster/FailoverClusterInvoker.java index 07c72fe44..bc624ffe5 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/cluster/FailoverClusterInvoker.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/cluster/FailoverClusterInvoker.java @@ -23,7 +23,6 @@ import com.jd.live.agent.core.inject.annotation.Inject; import com.jd.live.agent.core.inject.annotation.Injectable; import com.jd.live.agent.governance.exception.ErrorCause; -import com.jd.live.agent.governance.exception.ErrorPolicy; import com.jd.live.agent.governance.exception.RetryException.RetryExhaustedException; import com.jd.live.agent.governance.exception.RetryException.RetryTimeoutException; import com.jd.live.agent.governance.exception.ServiceError; @@ -33,12 +32,10 @@ import com.jd.live.agent.governance.policy.service.ServicePolicy; import com.jd.live.agent.governance.policy.service.cluster.ClusterPolicy; import com.jd.live.agent.governance.policy.service.cluster.RetryPolicy; -import com.jd.live.agent.governance.policy.service.exception.CodeParser; -import com.jd.live.agent.governance.request.Request; +import com.jd.live.agent.governance.policy.service.exception.ErrorParser; import com.jd.live.agent.governance.request.ServiceRequest.OutboundRequest; import com.jd.live.agent.governance.response.ServiceResponse.OutboundResponse; -import java.util.HashSet; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -61,7 +58,7 @@ public class FailoverClusterInvoker extends AbstractClusterInvoker { private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerFilter.class); @Inject - private Map codeParsers; + private Map codeParsers; @Override public CompletionStage execute(LiveCluster cluster, RetryPolicy retryPolicy = clusterPolicy == null ? null : clusterPolicy.getRetryPolicy(); retryPolicy = retryPolicy == null && defaultPolicy != null ? defaultPolicy.getRetryPolicy() : retryPolicy; R request = invocation.getRequest(); - if (retryPolicy != null && request.isDependentOnResponseBody(retryPolicy)) { - request.getAttributeIfAbsent(Request.KEY_ERROR_POLICY, k -> new HashSet()).add(retryPolicy); - } + request.addErrorPolicy(retryPolicy); RetryContext retryContext = new RetryContext<>(codeParsers, retryPolicy, cluster); Supplier> supplier = () -> invoke(cluster, invocation, retryContext.getCount()); cluster.onStart(request); @@ -105,7 +100,7 @@ private static class RetryContext { - private final Map errorParsers; + private final Map errorParsers; /** * The retry policy defining the rules for retrying the operation. @@ -133,7 +128,7 @@ private static class RetryContext errorParsers, RetryPolicy retryPolicy, LiveCluster cluster) { + RetryContext(Map errorParsers, RetryPolicy retryPolicy, LiveCluster cluster) { this.errorParsers = errorParsers; this.retryPolicy = retryPolicy; this.cluster = cluster; diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/filter/route/CircuitBreakerFilter.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/filter/route/CircuitBreakerFilter.java index 550b0924b..5558cce0c 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/filter/route/CircuitBreakerFilter.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/invoke/filter/route/CircuitBreakerFilter.java @@ -16,8 +16,6 @@ package com.jd.live.agent.governance.invoke.filter.route; import com.jd.live.agent.bootstrap.exception.RejectException.RejectCircuitBreakException; -import com.jd.live.agent.bootstrap.logger.Logger; -import com.jd.live.agent.bootstrap.logger.LoggerFactory; import com.jd.live.agent.core.extension.ExtensionInitializer; import com.jd.live.agent.core.extension.annotation.ConditionalOnProperty; import com.jd.live.agent.core.extension.annotation.Extension; @@ -27,7 +25,6 @@ import com.jd.live.agent.governance.config.GovernanceConfig; import com.jd.live.agent.governance.exception.CircuitBreakException; import com.jd.live.agent.governance.exception.ErrorCause; -import com.jd.live.agent.governance.exception.ErrorPolicy; import com.jd.live.agent.governance.instance.Endpoint; import com.jd.live.agent.governance.invoke.OutboundInvocation; import com.jd.live.agent.governance.invoke.OutboundListener; @@ -42,13 +39,11 @@ import com.jd.live.agent.governance.policy.service.ServicePolicy; import com.jd.live.agent.governance.policy.service.circuitbreak.CircuitBreakPolicy; import com.jd.live.agent.governance.policy.service.circuitbreak.DegradeConfig; -import com.jd.live.agent.governance.policy.service.exception.CodeParser; -import com.jd.live.agent.governance.request.Request; +import com.jd.live.agent.governance.policy.service.exception.ErrorParser; import com.jd.live.agent.governance.request.ServiceRequest.OutboundRequest; import com.jd.live.agent.governance.response.ServiceResponse; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -66,13 +61,11 @@ @ConditionalOnProperty(value = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true) public class CircuitBreakerFilter implements RouteFilter, ExtensionInitializer { - private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerFilter.class); - @Inject private Map factories; @Inject - private Map errorParsers; + private Map errorParsers; @Inject(nullable = true) private CircuitBreakerFactory defaultFactory; @@ -98,9 +91,7 @@ public void filter(OutboundInvocation invocation, CircuitBreaker breaker; T request = invocation.getRequest(); for (CircuitBreakPolicy policy : policies) { - if (request.isDependentOnResponseBody(policy)) { - request.getAttributeIfAbsent(Request.KEY_ERROR_POLICY, k -> new HashSet()).add(policy); - } + request.addErrorPolicy(policy); switch (policy.getLevel()) { case SERVICE: breaker = getCircuitBreaker(policy, policy.getUri()); @@ -213,7 +204,7 @@ private static class CircuitBreakerListener implements OutboundListener { private final CircuitBreakerFactory factory; - private final Map errorParsers; + private final Map errorParsers; private List circuitBreakers; @@ -223,7 +214,7 @@ private static class CircuitBreakerListener implements OutboundListener { CircuitBreakerListener(CircuitBreakerFactory factory, - Map errorParsers, + Map errorParsers, List circuitBreakers, List instancePolicies) { this.factory = factory; diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/circuitbreak/CircuitBreakPolicy.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/circuitbreak/CircuitBreakPolicy.java index b7dc9fc60..72fa3a876 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/circuitbreak/CircuitBreakPolicy.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/circuitbreak/CircuitBreakPolicy.java @@ -18,7 +18,7 @@ import com.jd.live.agent.governance.exception.ErrorPolicy; import com.jd.live.agent.governance.policy.PolicyId; import com.jd.live.agent.governance.policy.PolicyInherit; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; +import com.jd.live.agent.governance.policy.service.exception.ErrorParserPolicy; import lombok.Getter; import lombok.Setter; @@ -36,7 +36,6 @@ @Getter public class CircuitBreakPolicy extends PolicyId implements PolicyInherit.PolicyInheritWithIdGen, ErrorPolicy { - public static final String DEFAULT_CIRCUIT_BREAKER_TYPE = "Resilience4j"; public static final String SLIDING_WINDOW_TIME = "time"; public static final String SLIDING_WINDOW_COUNT = "count"; public static final int DEFAULT_FAILURE_RATE_THRESHOLD = 50; @@ -80,13 +79,23 @@ public class CircuitBreakPolicy extends PolicyId implements PolicyInherit.Policy /** * Code policy */ - private CodePolicy codePolicy; + private ErrorParserPolicy codePolicy; /** * Error code */ private Set errorCodes; + /** + * Error message policy + */ + private ErrorParserPolicy messagePolicy; + + /** + * Collection of error messages. This parameter specifies which status codes should be considered retryable. + */ + private Set errorMessages; + /** * Exception full class names. */ @@ -154,6 +163,10 @@ public void supplement(CircuitBreakPolicy source) { if (errorCodes == null && source.getErrorCodes() != null) { errorCodes = new HashSet<>(source.getErrorCodes()); } + messagePolicy = source.getMessagePolicy() == null ? null : source.getMessagePolicy().clone(); + if (errorMessages == null && source.getErrorMessages() != null) { + errorMessages = new HashSet<>(source.getErrorMessages()); + } if (exceptions == null && source.getExceptions() != null) { exceptions = new HashSet<>(source.getExceptions()); } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/cluster/RetryPolicy.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/cluster/RetryPolicy.java index b9b3a3bb3..cf4fc5e83 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/cluster/RetryPolicy.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/cluster/RetryPolicy.java @@ -19,10 +19,11 @@ import com.jd.live.agent.governance.policy.PolicyId; import com.jd.live.agent.governance.policy.PolicyInherit.PolicyInheritWithId; import com.jd.live.agent.governance.policy.service.annotation.Consumer; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; +import com.jd.live.agent.governance.policy.service.exception.ErrorParserPolicy; import lombok.Getter; import lombok.Setter; +import java.util.HashSet; import java.util.Set; /** @@ -59,15 +60,25 @@ public class RetryPolicy extends PolicyId implements PolicyInheritWithId errorCodes; + /** + * Error message policy + */ + private ErrorParserPolicy messagePolicy; + + /** + * Collection of retry error messages. This parameter specifies which status codes should be considered retryable. + */ + private Set errorMessages; + /** * A collection of retryable exception class names. */ @@ -101,16 +112,22 @@ public void supplement(RetryPolicy source) { codePolicy = source.codePolicy == null ? null : source.codePolicy.clone(); } if ((errorCodes == null || errorCodes.isEmpty()) && source.errorCodes != null) { - errorCodes = source.errorCodes; + errorCodes = new HashSet<>(source.errorCodes); + } + if (messagePolicy == null) { + messagePolicy = source.messagePolicy == null ? null : source.messagePolicy.clone(); + } + if ((errorMessages == null || errorMessages.isEmpty()) && source.errorMessages != null) { + errorMessages = new HashSet<>(source.errorMessages); } if ((exceptions == null || exceptions.isEmpty()) && source.exceptions != null) { - exceptions = source.exceptions; + exceptions = new HashSet<>(source.exceptions); } if ((methods == null || methods.isEmpty()) && source.methods != null) { - methods = source.methods; + methods = new HashSet<>(source.methods); } if ((methodPrefixes == null || methodPrefixes.isEmpty()) && source.methodPrefixes != null) { - methodPrefixes = source.methodPrefixes; + methodPrefixes = new HashSet<>(source.methodPrefixes); } } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/CodeParser.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/ErrorParser.java similarity index 90% rename from joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/CodeParser.java rename to joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/ErrorParser.java index 8ddfe351d..f44e6c415 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/CodeParser.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/ErrorParser.java @@ -20,8 +20,8 @@ /** * An interface for parsing code from service responses. */ -@Extensible("CodeParser") -public interface CodeParser { +@Extensible("ErrorParser") +public interface ErrorParser { /** * Parses the error from a service response using the provided expression. @@ -30,6 +30,6 @@ public interface CodeParser { * @param response The service response to parse. * @return The extracted error information as a string. */ - String getCode(String expression, Object response); + String getValue(String expression, Object response); } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/CodePolicy.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/ErrorParserPolicy.java similarity index 90% rename from joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/CodePolicy.java rename to joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/ErrorParserPolicy.java index b87413dbf..fc084c969 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/CodePolicy.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/ErrorParserPolicy.java @@ -24,13 +24,13 @@ import java.util.Set; /** - * A class representing a code policy. + * A class representing a error parser policy. */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor -public class CodePolicy implements Cloneable { +public class ErrorParserPolicy implements Cloneable { /** * Code parser @@ -82,16 +82,16 @@ public boolean match(String status, String contentType, String okStatus) { * * @return true if the body of the code should be parsed, false otherwise. */ - public boolean isDependentOnResponseBody() { + public boolean requireResponseBody() { return parser != null && expression != null && !parser.isEmpty() && !expression.isEmpty(); } @Override - public CodePolicy clone() { + public ErrorParserPolicy clone() { try { - return (CodePolicy) super.clone(); + return (ErrorParserPolicy) super.clone(); } catch (CloneNotSupportedException e) { - return new CodePolicy(parser, expression, statuses, contentTypes); + return new ErrorParserPolicy(parser, expression, statuses, contentTypes); } } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/json/JsonPathCodeParser.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/json/JsonPathErrorParser.java similarity index 89% rename from joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/json/JsonPathCodeParser.java rename to joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/json/JsonPathErrorParser.java index e30fd308c..35eb42c2a 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/json/JsonPathCodeParser.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/json/JsonPathErrorParser.java @@ -19,19 +19,19 @@ import com.jd.live.agent.core.inject.annotation.Inject; import com.jd.live.agent.core.inject.annotation.Injectable; import com.jd.live.agent.core.parser.JsonPathParser; -import com.jd.live.agent.governance.policy.service.exception.CodeParser; +import com.jd.live.agent.governance.policy.service.exception.ErrorParser; import java.io.InputStream; @Injectable @Extension("JsonPath") -public class JsonPathCodeParser implements CodeParser { +public class JsonPathErrorParser implements ErrorParser { @Inject private JsonPathParser parser; @Override - public String getCode(String expression, Object response) { + public String getValue(String expression, Object response) { if (expression == null || expression.isEmpty() || response == null) { return null; } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/value/ValuePathCodeParser.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/value/ValuePathErrorParser.java similarity index 84% rename from joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/value/ValuePathCodeParser.java rename to joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/value/ValuePathErrorParser.java index 9280da8c7..6ec18d692 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/value/ValuePathCodeParser.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/policy/service/exception/value/ValuePathErrorParser.java @@ -17,13 +17,13 @@ import com.jd.live.agent.core.extension.annotation.Extension; import com.jd.live.agent.core.util.type.ValuePath; -import com.jd.live.agent.governance.policy.service.exception.CodeParser; +import com.jd.live.agent.governance.policy.service.exception.ErrorParser; @Extension("ValuePath") -public class ValuePathCodeParser implements CodeParser { +public class ValuePathErrorParser implements ErrorParser { @Override - public String getCode(String expression, Object response) { + public String getValue(String expression, Object response) { if (expression == null || expression.isEmpty() || response == null) { return null; } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/AbstractHttpRequest.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/AbstractHttpRequest.java index 5fd4481e1..558e0a9ad 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/AbstractHttpRequest.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/AbstractHttpRequest.java @@ -18,11 +18,14 @@ import com.jd.live.agent.core.util.cache.UnsafeLazyObject; import com.jd.live.agent.core.util.network.IpLong; import com.jd.live.agent.core.util.network.IpType; +import com.jd.live.agent.governance.exception.ErrorPolicy; import lombok.Getter; import java.net.URI; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Provides an abstract base class for HTTP requests, implementing the {@link HttpRequest} interface. @@ -278,5 +281,17 @@ public abstract static class AbstractHttpOutboundRequest extends AbstractHttp public AbstractHttpOutboundRequest(T request) { super(request); } + + @Override + public void addErrorPolicy(ErrorPolicy policy) { + if (policy != null && policy.requireResponseBody()) { + getAttributeIfAbsent(KEY_ERROR_POLICY, k -> new HashSet()).add(policy); + } + } + + @Override + public Set removeErrorPolicies() { + return removeAttribute(KEY_ERROR_POLICY); + } } } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/ServiceRequest.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/ServiceRequest.java index 40961615a..481b5b9cd 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/ServiceRequest.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/request/ServiceRequest.java @@ -163,16 +163,6 @@ default boolean isSystem() { return false; } - /** - * Checks if the response body is required for the given error policy. - * - * @param policy the error policy to check - * @return true if the response body is required, false otherwise (default implementation always returns false) - */ - default boolean isDependentOnResponseBody(ErrorPolicy policy) { - return false; - } - /** * Rejects the request with the given fault type and reason. * @@ -307,6 +297,23 @@ default boolean isInstanceSensitive() { return true; } + /** + * Adds an error policy to the request if it requires the response body. + * + * @param policy the error policy to add + */ + default void addErrorPolicy(ErrorPolicy policy) { + } + + /** + * Removes all error policies from the request. + * + * @return a set of the removed error policies, or null if no policies were removed + */ + default Set removeErrorPolicies() { + return null; + } + /** * Returns the default error name function. * diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/response/HttpResponse.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/response/HttpResponse.java index 48f44746e..afaf449e5 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/response/HttpResponse.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/response/HttpResponse.java @@ -16,7 +16,7 @@ package com.jd.live.agent.governance.response; import com.jd.live.agent.governance.exception.ErrorPolicy; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; +import com.jd.live.agent.governance.policy.service.exception.ErrorParserPolicy; import java.net.URI; import java.util.List; @@ -113,7 +113,7 @@ default String getHeader(String key) { @Override default boolean match(ErrorPolicy errorPolicy) { - CodePolicy codePolicy = errorPolicy == null ? null : errorPolicy.getCodePolicy(); + ErrorParserPolicy codePolicy = errorPolicy == null ? null : errorPolicy.getCodePolicy(); return codePolicy != null && codePolicy.match(getCode(), getHeader(CONTENT_TYPE), String.valueOf(OK.value())); } diff --git a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/util/Predicates.java b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/util/Predicates.java index d68bf271f..e1bdfc324 100644 --- a/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/util/Predicates.java +++ b/joylive-core/joylive-governance-api/src/main/java/com/jd/live/agent/governance/util/Predicates.java @@ -20,8 +20,8 @@ import com.jd.live.agent.governance.exception.ErrorCause; import com.jd.live.agent.governance.exception.ErrorPolicy; import com.jd.live.agent.governance.exception.ErrorPredicate; -import com.jd.live.agent.governance.policy.service.exception.CodeParser; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; +import com.jd.live.agent.governance.policy.service.exception.ErrorParser; +import com.jd.live.agent.governance.policy.service.exception.ErrorParserPolicy; import com.jd.live.agent.governance.request.ServiceRequest.OutboundRequest; import com.jd.live.agent.governance.response.ServiceResponse; @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import static com.jd.live.agent.governance.exception.ErrorCause.cause; @@ -77,7 +78,7 @@ public static boolean isError(ErrorPolicy policy, OutboundRequest request, ServiceResponse response, ErrorPredicate predicate, - Function factory) { + Function factory) { if (policy.containsErrorCode(response.getCode())) { return true; } @@ -91,8 +92,28 @@ public static boolean isError(ErrorPolicy policy, } } if (response.match(policy)) { - String code = parseCode(policy.getCodePolicy(), response.getResult(), factory); - return policy.containsErrorCode(code); + if (isError(policy.getCodePolicy(), response, factory, policy::containsErrorCode)) return true; + if (isError(policy.getMessagePolicy(), response, factory, policy::containsErrorMessage)) return true; + } + return false; + } + + /** + * Checks if the given response matches the error condition specified by the error parser policy. + * + * @param policy the error parser policy + * @param response the service response + * @param factory the error parser factory + * @param predicate the predicate to apply to the parsed value + * @return true if the response matches the error condition, false otherwise + */ + private static boolean isError(ErrorParserPolicy policy, + ServiceResponse response, + Function factory, + Predicate predicate) { + if (policy != null) { + String value = parseValue(policy, response.getResult(), factory); + return predicate.test(value); } return false; } @@ -105,23 +126,25 @@ public static boolean isError(ErrorPolicy policy, * @param factory the code parser factory * @return the parsed error code, or null if no code could be parsed */ - private static String parseCode(CodePolicy policy, Object result, Function factory) { - String code = null; + private static String parseValue(ErrorParserPolicy policy, + Object result, + Function factory) { + String value = null; if (policy != null && result != null) { String errorParser = policy.getParser(); String errorExpression = policy.getExpression(); if (errorParser != null && !errorParser.isEmpty() && errorExpression != null && !errorExpression.isEmpty()) { - CodeParser parser = factory.apply(errorParser); + ErrorParser parser = factory.apply(errorParser); if (parser != null) { try { - code = parser.getCode(errorExpression, result); + value = parser.getValue(errorExpression, result); } catch (Throwable e) { logger.error(e.getMessage(), e); } } } } - return code; + return value; } } diff --git a/joylive-core/joylive-governance-api/src/main/resources/META-INF/services/com.jd.live.agent.governance.policy.service.exception.CodeParser b/joylive-core/joylive-governance-api/src/main/resources/META-INF/services/com.jd.live.agent.governance.policy.service.exception.ErrorParser similarity index 80% rename from joylive-core/joylive-governance-api/src/main/resources/META-INF/services/com.jd.live.agent.governance.policy.service.exception.CodeParser rename to joylive-core/joylive-governance-api/src/main/resources/META-INF/services/com.jd.live.agent.governance.policy.service.exception.ErrorParser index e13151be6..599fab735 100644 --- a/joylive-core/joylive-governance-api/src/main/resources/META-INF/services/com.jd.live.agent.governance.policy.service.exception.CodeParser +++ b/joylive-core/joylive-governance-api/src/main/resources/META-INF/services/com.jd.live.agent.governance.policy.service.exception.ErrorParser @@ -1,2 +1,2 @@ -com.jd.live.agent.governance.policy.service.exception.json.JsonPathCodeParser -com.jd.live.agent.governance.policy.service.exception.value.ValuePathCodeParser \ No newline at end of file +com.jd.live.agent.governance.policy.service.exception.json.JsonPathErrorParser +com.jd.live.agent.governance.policy.service.exception.value.ValuePathErrorParser \ No newline at end of file diff --git a/joylive-plugin/joylive-router/joylive-router-springcloud2/src/main/java/com/jd/live/agent/plugin/router/springcloud/v2/request/AbstractClusterRequest.java b/joylive-plugin/joylive-router/joylive-router-springcloud2/src/main/java/com/jd/live/agent/plugin/router/springcloud/v2/request/AbstractClusterRequest.java index d3c115fc5..99d1f13bb 100644 --- a/joylive-plugin/joylive-router/joylive-router-springcloud2/src/main/java/com/jd/live/agent/plugin/router/springcloud/v2/request/AbstractClusterRequest.java +++ b/joylive-plugin/joylive-router/joylive-router-springcloud2/src/main/java/com/jd/live/agent/plugin/router/springcloud/v2/request/AbstractClusterRequest.java @@ -17,7 +17,6 @@ import com.jd.live.agent.core.util.cache.CacheObject; import com.jd.live.agent.core.util.cache.UnsafeLazyObject; -import com.jd.live.agent.governance.exception.ErrorPolicy; import com.jd.live.agent.governance.request.AbstractHttpRequest.AbstractHttpOutboundRequest; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.client.ServiceInstance; @@ -83,11 +82,6 @@ public String getCookie(String key) { } } - @Override - public boolean isDependentOnResponseBody(ErrorPolicy policy) { - return policy != null && policy.getCodePolicy() != null && policy.getCodePolicy().isDependentOnResponseBody(); - } - public ServiceInstanceListSupplier getInstanceSupplier() { return instanceSupplier.get(); } diff --git a/joylive-plugin/joylive-router/joylive-router-springcloud3/src/main/java/com/jd/live/agent/plugin/router/springcloud/v3/request/AbstractClusterRequest.java b/joylive-plugin/joylive-router/joylive-router-springcloud3/src/main/java/com/jd/live/agent/plugin/router/springcloud/v3/request/AbstractClusterRequest.java index 259558203..8e610022c 100644 --- a/joylive-plugin/joylive-router/joylive-router-springcloud3/src/main/java/com/jd/live/agent/plugin/router/springcloud/v3/request/AbstractClusterRequest.java +++ b/joylive-plugin/joylive-router/joylive-router-springcloud3/src/main/java/com/jd/live/agent/plugin/router/springcloud/v3/request/AbstractClusterRequest.java @@ -17,7 +17,6 @@ import com.jd.live.agent.core.util.cache.CacheObject; import com.jd.live.agent.core.util.cache.UnsafeLazyObject; -import com.jd.live.agent.governance.exception.ErrorPolicy; import com.jd.live.agent.governance.request.AbstractHttpRequest.AbstractHttpOutboundRequest; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.client.ServiceInstance; @@ -140,11 +139,6 @@ public void lifecycles(Consumer consumer) { } } - @Override - public boolean isDependentOnResponseBody(ErrorPolicy policy) { - return policy != null && policy.getCodePolicy() != null && policy.getCodePolicy().isDependentOnResponseBody(); - } - public Request getLbRequest() { return lbRequest.get(); } diff --git a/joylive-plugin/joylive-router/joylive-router-springcloud4/src/main/java/com/jd/live/agent/plugin/router/springcloud/v4/request/AbstractClusterRequest.java b/joylive-plugin/joylive-router/joylive-router-springcloud4/src/main/java/com/jd/live/agent/plugin/router/springcloud/v4/request/AbstractClusterRequest.java index 3c0b4ed5d..377ca6c7c 100644 --- a/joylive-plugin/joylive-router/joylive-router-springcloud4/src/main/java/com/jd/live/agent/plugin/router/springcloud/v4/request/AbstractClusterRequest.java +++ b/joylive-plugin/joylive-router/joylive-router-springcloud4/src/main/java/com/jd/live/agent/plugin/router/springcloud/v4/request/AbstractClusterRequest.java @@ -19,7 +19,6 @@ import com.jd.live.agent.core.util.cache.UnsafeLazyObject; import com.jd.live.agent.core.util.type.FieldDesc; import com.jd.live.agent.core.util.type.FieldList; -import com.jd.live.agent.governance.exception.ErrorPolicy; import com.jd.live.agent.governance.request.AbstractHttpRequest.AbstractHttpOutboundRequest; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.client.ServiceInstance; @@ -145,11 +144,6 @@ public void lifecycles(Consumer consumer) { } } - @Override - public boolean isDependentOnResponseBody(ErrorPolicy policy) { - return policy != null && policy.getCodePolicy() != null && policy.getCodePolicy().isDependentOnResponseBody(); - } - public Request getLbRequest() { return lbRequest.get(); } diff --git a/joylive-plugin/joylive-router/joylive-router-springgateway2/src/main/java/com/jd/live/agent/plugin/router/springgateway/v2/cluster/GatewayCluster.java b/joylive-plugin/joylive-router/joylive-router-springgateway2/src/main/java/com/jd/live/agent/plugin/router/springgateway/v2/cluster/GatewayCluster.java index 549c3c255..d838a0a2d 100644 --- a/joylive-plugin/joylive-router/joylive-router-springgateway2/src/main/java/com/jd/live/agent/plugin/router/springgateway/v2/cluster/GatewayCluster.java +++ b/joylive-plugin/joylive-router/joylive-router-springgateway2/src/main/java/com/jd/live/agent/plugin/router/springgateway/v2/cluster/GatewayCluster.java @@ -24,7 +24,6 @@ import com.jd.live.agent.governance.policy.service.circuitbreak.DegradeConfig; import com.jd.live.agent.governance.policy.service.cluster.ClusterPolicy; import com.jd.live.agent.governance.policy.service.cluster.RetryPolicy; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; import com.jd.live.agent.governance.request.Request; import com.jd.live.agent.plugin.router.springcloud.v2.cluster.AbstractClientCluster; import com.jd.live.agent.plugin.router.springcloud.v2.instance.SpringEndpoint; @@ -105,7 +104,7 @@ public ClusterPolicy getDefaultPolicy(GatewayClusterRequest request) { @Override public CompletionStage invoke(GatewayClusterRequest request, SpringEndpoint endpoint) { try { - Set policies = request.removeAttribute(Request.KEY_ERROR_POLICY); + Set policies = request.removeErrorPolicies(); // decorate request to transmission Consumer header = b -> b.headers(headers -> RequestContext.cargos(headers::set)); ServerWebExchange.Builder builder = request.getExchange().mutate().request(header); @@ -235,10 +234,10 @@ public Mono writeWith(@NonNull Publisher body) { */ private boolean policyMatch(String contentType) { contentType = contentType == null ? null : contentType.toLowerCase(); - CodePolicy codePolicy; + Integer status = getRawStatusCode(); + int ok = HttpStatus.OK.value(); for (ErrorPolicy policy : policies) { - codePolicy = policy.getCodePolicy(); - if (codePolicy != null && codePolicy.match(getRawStatusCode(), contentType, HttpStatus.OK.value())) { + if (policy.match(status, contentType, ok)) { return true; } } diff --git a/joylive-plugin/joylive-router/joylive-router-springgateway3/src/main/java/com/jd/live/agent/plugin/router/springgateway/v3/cluster/GatewayCluster.java b/joylive-plugin/joylive-router/joylive-router-springgateway3/src/main/java/com/jd/live/agent/plugin/router/springgateway/v3/cluster/GatewayCluster.java index 44ab76fdf..f893b74a9 100644 --- a/joylive-plugin/joylive-router/joylive-router-springgateway3/src/main/java/com/jd/live/agent/plugin/router/springgateway/v3/cluster/GatewayCluster.java +++ b/joylive-plugin/joylive-router/joylive-router-springgateway3/src/main/java/com/jd/live/agent/plugin/router/springgateway/v3/cluster/GatewayCluster.java @@ -23,7 +23,6 @@ import com.jd.live.agent.governance.policy.service.circuitbreak.DegradeConfig; import com.jd.live.agent.governance.policy.service.cluster.ClusterPolicy; import com.jd.live.agent.governance.policy.service.cluster.RetryPolicy; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; import com.jd.live.agent.governance.request.Request; import com.jd.live.agent.plugin.router.springcloud.v3.cluster.AbstractClientCluster; import com.jd.live.agent.plugin.router.springcloud.v3.instance.SpringEndpoint; @@ -111,7 +110,7 @@ protected boolean isRetryable() { @Override public CompletionStage invoke(GatewayClusterRequest request, SpringEndpoint endpoint) { try { - Set policies = request.removeAttribute(Request.KEY_ERROR_POLICY); + Set policies = request.removeErrorPolicies(); // decorate response to remove exception header and get body BodyResponseDecorator decorator = new BodyResponseDecorator(request.getExchange(), policies); ServerWebExchange exchange = request.getExchange().mutate().response(decorator).build(); @@ -252,10 +251,10 @@ public Mono writeWith(@NonNull Publisher body) { */ private boolean policyMatch(String contentType) { contentType = contentType == null ? null : contentType.toLowerCase(); - CodePolicy codePolicy; + Integer status = getRawStatusCode(); + int ok = HttpStatus.OK.value(); for (ErrorPolicy policy : policies) { - codePolicy = policy.getCodePolicy(); - if (codePolicy != null && codePolicy.match(getRawStatusCode(), contentType, HttpStatus.OK.value())) { + if (policy.match(status, contentType, ok)) { return true; } } diff --git a/joylive-plugin/joylive-router/joylive-router-springgateway4/src/main/java/com/jd/live/agent/plugin/router/springgateway/v4/cluster/GatewayCluster.java b/joylive-plugin/joylive-router/joylive-router-springgateway4/src/main/java/com/jd/live/agent/plugin/router/springgateway/v4/cluster/GatewayCluster.java index dda31d95f..af91491c7 100644 --- a/joylive-plugin/joylive-router/joylive-router-springgateway4/src/main/java/com/jd/live/agent/plugin/router/springgateway/v4/cluster/GatewayCluster.java +++ b/joylive-plugin/joylive-router/joylive-router-springgateway4/src/main/java/com/jd/live/agent/plugin/router/springgateway/v4/cluster/GatewayCluster.java @@ -23,7 +23,6 @@ import com.jd.live.agent.governance.policy.service.circuitbreak.DegradeConfig; import com.jd.live.agent.governance.policy.service.cluster.ClusterPolicy; import com.jd.live.agent.governance.policy.service.cluster.RetryPolicy; -import com.jd.live.agent.governance.policy.service.exception.CodePolicy; import com.jd.live.agent.governance.request.Request; import com.jd.live.agent.plugin.router.springcloud.v4.cluster.AbstractClientCluster; import com.jd.live.agent.plugin.router.springcloud.v4.instance.SpringEndpoint; @@ -112,7 +111,7 @@ protected boolean isRetryable() { @Override public CompletionStage invoke(GatewayClusterRequest request, SpringEndpoint endpoint) { try { - Set policies = request.removeAttribute(Request.KEY_ERROR_POLICY); + Set policies = request.removeErrorPolicies(); // decorate response to remove exception header and get body BodyResponseDecorator decorator = new BodyResponseDecorator(request.getExchange(), policies); ServerWebExchange exchange = request.getExchange().mutate().response(decorator).build(); @@ -252,12 +251,11 @@ public Mono writeWith(@NonNull Publisher body) { */ private boolean policyMatch(String contentType) { contentType = contentType == null ? null : contentType.toLowerCase(); - CodePolicy codePolicy; HttpStatusCode statusCode = getStatusCode(); Integer status = statusCode == null ? null : statusCode.value(); + int ok = HttpStatus.OK.value(); for (ErrorPolicy policy : policies) { - codePolicy = policy.getCodePolicy(); - if (codePolicy != null && codePolicy.match(status, contentType, HttpStatus.OK.value())) { + if (policy.match(status, contentType, ok)) { return true; } }