java 在使用 Feign 和 Hystrix 时,如何允许 400 错误传播?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42730881/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you allow 400 Errors to propagate when using Feign with Hystrix?
提问by peterl
I'm building a SpringBoot microservice that calls another microservice and naturally want to use Hystrix and Feign clients, which are both included with Spring Cloud. I'm using version Camden.SR5.
我正在构建一个调用另一个微服务的 SpringBoot 微服务,自然想使用 Hystrix 和 Feign 客户端,它们都包含在 Spring Cloud 中。我正在使用版本 Camden.SR5。
For any timeouts, connection failures and 50x response codes from Feign, I want Hystrix to kick in and work as normal: tripping the circuit breaker and calling the fallback (if configured), etc. It does this by default, so I'm good.
对于来自 Feign 的任何超时、连接失败和 50x 响应代码,我希望 Hystrix 启动并正常工作:跳闸断路器并调用回退(如果已配置)等。它默认执行此操作,所以我很好.
But for 40x response codes, which include things like invalid entry, the wrong format of fields etc, I want Hystrix to propagate these exceptions to the caller, so I can handle them as I choose too. This isn't the default I've observed. How do you configure Hystrix/Feign to do this in Spring Cloud?
但是对于 40x 响应代码,其中包括无效条目、错误的字段格式等,我希望 Hystrix 将这些异常传播给调用者,这样我也可以按照自己的选择处理它们。这不是我观察到的默认值。你如何配置 Hystrix/Feign 在 Spring Cloud 中做到这一点?
Out of the box using the following code:
使用以下代码开箱即用:
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);
}
Generates this exception, which doesn't lend itself to nicely passing that 40x response back to the caller:
生成此异常,它不利于将 40x 响应很好地传递回调用者:
com.netflix.hystrix.exception.HystrixRuntimeException: DogsFeignClient#createDog(Dog) failed and no fallback available.
at com.netflix.hystrix.AbstractCommand.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.run(HystrixInvocationHandler.java:108) ~[feign-hystrix-9.3.1.jar:na]
at com.netflix.hystrix.HystrixCommand.call(HystrixCommand.java:301) ~[hystrix-core-1.5.6.jar:1.5.6]
at com.netflix.hystrix.HystrixCommand.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
I can of course look at the com.netflix.hystrix.exception.HystrixRuntimeException
, cause
field which contains a feign.FeignException
and buried in the description is the JSON response itself, with line breaks and such. But the cause
field of feign.FeignException
is a reference to itself. Is there a way to get a deeper exception propagated instead of the HystrixRuntimeException?
我当然可以查看包含 a的com.netflix.hystrix.exception.HystrixRuntimeException
,cause
字段,feign.FeignException
并且隐藏在描述中的是 JSON 响应本身,带有换行符等。但是cause
域feign.FeignException
是对自身的引用。有没有办法传播更深层次的异常而不是 HystrixRuntimeException?
Also is there a way to get the raw body included with the response from the downstream service, so I don't have to deconstruct the message field of the nested exception?
还有没有办法从下游服务的响应中获取原始正文,所以我不必解构嵌套异常的消息字段?
回答by jihor
This can be achieved using a separate configuration, which will wrap 400's in a subclass of HystrixBadRequestException
and throw them to the client code.
These exceptions don't affect the circuit breaker state - if the circuit is closed, it will remain closed, and if it's open, it will remain open.
这可以使用单独的配置来实现,它将 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);
}
where FeignPropagateBadRequestsConfiguration
is
这里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);
}
};
}
}
and FeignBadResponseWrapper
is
并且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;
}
}
This is a bit of a hack, and you can get the response body only in ErrorDecoder
, because after that the stream will be closed. But using this, you can throw the response data to client code without affecting the circuit:
这有点麻烦,您只能在 中获得响应正文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
}
}