Search in sources :

Example 1 with Repeat

use of reactor.retry.Repeat in project spring-cloud-gateway by spring-cloud.

the class RetryGatewayFilterFactory method apply.

@Override
public GatewayFilter apply(RetryConfig retryConfig) {
    retryConfig.validate();
    Repeat<ServerWebExchange> statusCodeRepeat = null;
    if (!retryConfig.getStatuses().isEmpty() || !retryConfig.getSeries().isEmpty()) {
        Predicate<RepeatContext<ServerWebExchange>> repeatPredicate = context -> {
            ServerWebExchange exchange = context.applicationContext();
            if (exceedsMaxIterations(exchange, retryConfig)) {
                return false;
            }
            HttpStatusCode statusCode = exchange.getResponse().getStatusCode();
            boolean retryableStatusCode = retryConfig.getStatuses().contains(statusCode);
            // null status code might mean a network exception?
            if (!retryableStatusCode && statusCode != null) {
                // try the series
                retryableStatusCode = false;
                for (int i = 0; i < retryConfig.getSeries().size(); i++) {
                    if (statusCode instanceof HttpStatus) {
                        HttpStatus httpStatus = (HttpStatus) statusCode;
                        if (httpStatus.series().equals(retryConfig.getSeries().get(i))) {
                            retryableStatusCode = true;
                            break;
                        }
                    }
                }
            }
            final boolean finalRetryableStatusCode = retryableStatusCode;
            trace("retryableStatusCode: %b, statusCode %s, configured statuses %s, configured series %s", () -> finalRetryableStatusCode, () -> statusCode, retryConfig::getStatuses, retryConfig::getSeries);
            HttpMethod httpMethod = exchange.getRequest().getMethod();
            boolean retryableMethod = retryConfig.getMethods().contains(httpMethod);
            trace("retryableMethod: %b, httpMethod %s, configured methods %s", () -> retryableMethod, () -> httpMethod, retryConfig::getMethods);
            return retryableMethod && finalRetryableStatusCode;
        };
        statusCodeRepeat = Repeat.onlyIf(repeatPredicate).doOnRepeat(context -> reset(context.applicationContext()));
        BackoffConfig backoff = retryConfig.getBackoff();
        if (backoff != null) {
            statusCodeRepeat = statusCodeRepeat.backoff(getBackoff(backoff));
        }
    }
    // TODO: support timeout, backoff, jitter, etc... in Builder
    Retry<ServerWebExchange> exceptionRetry = null;
    if (!retryConfig.getExceptions().isEmpty()) {
        Predicate<RetryContext<ServerWebExchange>> retryContextPredicate = context -> {
            ServerWebExchange exchange = context.applicationContext();
            if (exceedsMaxIterations(exchange, retryConfig)) {
                return false;
            }
            Throwable exception = context.exception();
            for (Class<? extends Throwable> retryableClass : retryConfig.getExceptions()) {
                if (retryableClass.isInstance(exception) || (exception != null && retryableClass.isInstance(exception.getCause()))) {
                    trace("exception or its cause is retryable %s, configured exceptions %s", () -> getExceptionNameWithCause(exception), retryConfig::getExceptions);
                    HttpMethod httpMethod = exchange.getRequest().getMethod();
                    boolean retryableMethod = retryConfig.getMethods().contains(httpMethod);
                    trace("retryableMethod: %b, httpMethod %s, configured methods %s", () -> retryableMethod, () -> httpMethod, retryConfig::getMethods);
                    return retryableMethod;
                }
            }
            trace("exception or its cause is not retryable %s, configured exceptions %s", () -> getExceptionNameWithCause(exception), retryConfig::getExceptions);
            return false;
        };
        exceptionRetry = Retry.onlyIf(retryContextPredicate).doOnRetry(context -> reset(context.applicationContext())).retryMax(retryConfig.getRetries());
        BackoffConfig backoff = retryConfig.getBackoff();
        if (backoff != null) {
            exceptionRetry = exceptionRetry.backoff(getBackoff(backoff));
        }
    }
    GatewayFilter gatewayFilter = apply(retryConfig.getRouteId(), statusCodeRepeat, exceptionRetry);
    return new GatewayFilter() {

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return gatewayFilter.filter(exchange, chain);
        }

        @Override
        public String toString() {
            return filterToStringCreator(RetryGatewayFilterFactory.this).append("routeId", retryConfig.getRouteId()).append("retries", retryConfig.getRetries()).append("series", retryConfig.getSeries()).append("statuses", retryConfig.getStatuses()).append("methods", retryConfig.getMethods()).append("exceptions", retryConfig.getExceptions()).toString();
        }
    };
}
Also used : GatewayToStringStyler.filterToStringCreator(org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator) EnableBodyCachingEvent(org.springframework.cloud.gateway.event.EnableBodyCachingEvent) TimeoutException(org.springframework.cloud.gateway.support.TimeoutException) Arrays(java.util.Arrays) GatewayFilterChain(org.springframework.cloud.gateway.filter.GatewayFilterChain) RetryContext(reactor.retry.RetryContext) Series(org.springframework.http.HttpStatus.Series) ServerWebExchangeUtils(org.springframework.cloud.gateway.support.ServerWebExchangeUtils) HasRouteId(org.springframework.cloud.gateway.support.HasRouteId) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) ServerWebExchange(org.springframework.web.server.ServerWebExchange) Duration(java.time.Duration) Retry(reactor.retry.Retry) Connection(reactor.netty.Connection) Predicate(java.util.function.Predicate) Publisher(org.reactivestreams.Publisher) Backoff(reactor.retry.Backoff) HttpStatusCode(org.springframework.http.HttpStatusCode) HttpMethod(org.springframework.http.HttpMethod) IOException(java.io.IOException) Mono(reactor.core.publisher.Mono) Repeat(reactor.retry.Repeat) RepeatContext(reactor.retry.RepeatContext) HttpStatus(org.springframework.http.HttpStatus) List(java.util.List) GatewayFilter(org.springframework.cloud.gateway.filter.GatewayFilter) Log(org.apache.commons.logging.Log) LogFactory(org.apache.commons.logging.LogFactory) Assert(org.springframework.util.Assert) ServerWebExchange(org.springframework.web.server.ServerWebExchange) HttpStatus(org.springframework.http.HttpStatus) RetryContext(reactor.retry.RetryContext) RepeatContext(reactor.retry.RepeatContext) HttpStatusCode(org.springframework.http.HttpStatusCode) GatewayFilterChain(org.springframework.cloud.gateway.filter.GatewayFilterChain) HttpMethod(org.springframework.http.HttpMethod) GatewayFilter(org.springframework.cloud.gateway.filter.GatewayFilter)

Aggregations

IOException (java.io.IOException)1 Duration (java.time.Duration)1 ArrayList (java.util.ArrayList)1 Arrays (java.util.Arrays)1 List (java.util.List)1 Predicate (java.util.function.Predicate)1 Supplier (java.util.function.Supplier)1 Log (org.apache.commons.logging.Log)1 LogFactory (org.apache.commons.logging.LogFactory)1 Publisher (org.reactivestreams.Publisher)1 EnableBodyCachingEvent (org.springframework.cloud.gateway.event.EnableBodyCachingEvent)1 GatewayFilter (org.springframework.cloud.gateway.filter.GatewayFilter)1 GatewayFilterChain (org.springframework.cloud.gateway.filter.GatewayFilterChain)1 GatewayToStringStyler.filterToStringCreator (org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator)1 HasRouteId (org.springframework.cloud.gateway.support.HasRouteId)1 ServerWebExchangeUtils (org.springframework.cloud.gateway.support.ServerWebExchangeUtils)1 TimeoutException (org.springframework.cloud.gateway.support.TimeoutException)1 HttpMethod (org.springframework.http.HttpMethod)1 HttpStatus (org.springframework.http.HttpStatus)1 Series (org.springframework.http.HttpStatus.Series)1