在使用Feys with Hystrix时,如何允许400个错误传播?

pet*_*erl 6 java spring hystrix spring-cloud spring-cloud-feign

我正在构建一个调用另一个微服务的SpringBoot微服务,并且自然希望使用Hystrix和Feign客户端,它们都包含在Spring Cloud中.我正在使用版本Camden.SR5.

对于Feign的任何超时,连接失败和50x响应代码,我希望Hystrix能够正常启动并正常工作:绊倒断路器并调用回退(如果已配置)等.默认情况下这样做,所以我很好.

但对于40x响应代码,包括无效输入,错误的字段格式等,我希望Hystrix将这些异常传播给调用者,所以我也可以按照我的选择来处理它们.这不是我观察到的默认值.如何配置Hystrix/Feign在Spring Cloud中执行此操作?

使用以下代码开箱即用:

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.hateoas.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "dog-service", url = "http://...")
public interface DogsFeignClient {
  @RequestMapping(method = RequestMethod.POST, path = "/dogs")
  Resource<Dog> createDog(Dog dog);
}
Run Code Online (Sandbox Code Playgroud)

生成此异常,这不适合将40x响应很好地传递给调用者:

com.netflix.hystrix.exception.HystrixRuntimeException: DogsFeignClient#createDog(Dog) failed and no fallback available.
    at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:805) ~[hystrix-core-1.5.6.jar:1.5.6]
    ....lines ommited for brevity....
Caused by: feign.FeignException: status 400 reading DogsFeignClient#createDog(Dog); content:
{
  "errors" : [ {
    "entity" : "Dog",
    "property" : "numberOfLegs",
    "invalidValue" : "3",
    "message" : "All dogs must have 4 legs"
  } ]
}
    at feign.FeignException.errorStatus(FeignException.java:62) ~[feign-core-9.3.1.jar:na]
    at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:91) ~[feign-core-9.3.1.jar:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138) ~[feign-core-9.3.1.jar:na]
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-9.3.1.jar:na]
    at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) ~[feign-hystrix-9.3.1.jar:na]
    at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:301) ~[hystrix-core-1.5.6.jar:1.5.6]
    at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:297) ~[hystrix-core-1.5.6.jar:1.5.6]
    at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.1.10.jar:1.1.10]
    ... 26 common frames omitted
Run Code Online (Sandbox Code Playgroud)

当然,我可以看一下com.netflix.hystrix.exception.HystrixRuntimeException,cause其中包含一个字段feign.FeignException,并埋在介绍JSON响应本身,换行和这样的.但是这个cause领域feign.FeignException是对自己的参考.有没有办法获得更深层次的异常传播而不是HystrixRuntimeException?

还有一种方法可以将原始主体包含在下游服务的响应中,因此我不必解构嵌套异常的消息字段吗?

jih*_*hor 5

这可以使用单独的配置来实现,该配置将400包装在子类中HystrixBadRequestException并将它们抛出到客户端代码中.这些例外不影响断路器状态 - 如果电路闭合,它将保持闭合,如果它打开,它将保持打开状态.

@FeignClient(name = "dog-service", 
             url = "http://...", 
             configuration=FeignPropagateBadRequestsConfiguration.class)
public interface DogsFeignClient {
  @RequestMapping(method = RequestMethod.POST, path = "/dogs")
  Resource<Dog> createDog(Dog dog);
}
Run Code Online (Sandbox Code Playgroud)

这里FeignPropagateBadRequestsConfiguration

@Configuration
public class FeignSkipBadRequestsConfiguration {
    @Bean
    public ErrorDecoder errorDecoder() {
        return (methodKey, response) -> {
            int status = response.status();
            if (status == 400) {
                String body = "Bad request";
                try {
                    body = IOUtils.toString(response.body().asReader());
                } catch (Exception ignored) {}
                HttpHeaders httpHeaders = new HttpHeaders();
                response.headers().forEach((k, v) -> httpHeaders.add("feign-" + k, StringUtils.join(v,",")));
                return new FeignBadResponseWrapper(status, httpHeaders, body);
            }
            else {
                return new RuntimeException("Response Code " + status);
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

并且FeignBadResponseWrapper

@Getter
@Setter
public class FeignBadResponseWrapper extends HystrixBadRequestException {
    private final int status;
    private final HttpHeaders headers;
    private final String body;

    public FeignBadResponseWrapper(int status, HttpHeaders headers, String body) {
        super("Bad request");
        this.status = status;
        this.headers = headers;
        this.body = body;
    }
}
Run Code Online (Sandbox Code Playgroud)

这有点像黑客攻击,你只能进入响应体ErrorDecoder,因为之后流将被关闭.但是使用它,您可以将响应数据抛出到客户端代码而不会影响电路:

    try {
        return dogsFeignClient.createDog(dog);
    } catch (HystrixBadRequestException he) {
        if (he instanceof FeignBadResponseWrapper) {
            // obtain data from wrapper and return it to client
        } else {
            // return basic error data for other exceptions
        }
    }
Run Code Online (Sandbox Code Playgroud)