Obtendo Exceções do Hystrix em Solicitações FallBack no Spring Cloud Gateway

Objetivo

O Spring Cloud Gateway está configurado com um timeout de disjuntor (circuit breaker):

# hystrix - timeout automático após 10 segundos
hystrix.command.fallbackCmd.execution.isolation.thread.timeoutInMilliseconds=10000

Quando ocorre um timeout, a lógica configurada no fallbackUri é executada. Existe a necessidade de retornar informações detalhadas sobre o "timeout da solicitação" em vez de uma mensagem genérica de "serviço indispnoível". Para isso, é preciso obter a exceção detalhada dentro desse método.

Aálise do Código Fonte

Analisando o método org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory.RouteHystrixCommand#resumeWithFallback:

@Override
protected Observable<Void> resumeWithFallback() {
    if (this.fallbackUri == null) {
        return super.resumeWithFallback();
    }

    // TODO: copied from RouteToRequestUrlFilter
    URI uri = exchange.getRequest().getURI();
    // TODO: assume always?
    boolean encoded = containsEncodedParts(uri);
    URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null)
            .uri(this.fallbackUri).scheme(null).build(encoded).toUri();
    exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
    addExceptionDetails();

    ServerHttpRequest request = this.exchange.getRequest().mutate()
            .uri(requestUrl).build();
    ServerWebExchange mutated = exchange.mutate().request(request).build();
    // Before we continue on remove the already routed attribute since the
    // fallback may go back through the route handler if the fallback
    // is to another route in the Gateway
    removeAlreadyRouted(mutated);
    return RxReactiveStreams.toObservable(getDispatcherHandler().handle(mutated));
}

É possível observar que o HystrixGatewayFilter, ao processar a lógica de fallback, armazena as informações da exceção nos atributos do exchange através do atributo org.springframework.cloud.gateway.support.ServerWebExchangeUtils#GATEWAY_REQUEST_URL_ATTR. Portanto, basta recuperar esses detalhes diretamente no controller.

Exemplo Prático

@Log4j2
@RestController
public class HystrixFallbackController {

    /**
     * Tratamento de degradação de serviço
     *
     * @return ResponseVO
     */
    @RequestMapping("/defaultFallback")
    public ResponseVO<Void> handleFallback(ServerWebExchange exchange) {
        log.warn("Degradação de serviço...{}", Objects.toString(
            exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR)));

        Exception ex = exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR);
        
        if (ex instanceof HystrixTimeoutException) {
            return ResponseFactory.errorResponseFromMsg("Timeout na solicitação do gateway...");
        }

        return ResponseFactory.errorResponseFromMsg("Serviço temporariamente indisponível, tente novamente mais tarde...");
    }
}

Publicado em 10-18 06:24