我正在构建一个调用另一个微服务的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);
}
生成此异常,它不能很好地将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
我当然可以查看包含com.netflix.hystrix.exception.HystrixRuntimeException
的{{1}},cause
字段,并在说明中隐藏JSON响应本身,并使用换行符等。但feign.FeignException
的{{1}}字段是对自身的引用。有没有办法让传播更深层的异常而不是HystrixRuntimeException?
还有一种方法可以将原始主体包含在下游服务的响应中,因此我不必解构嵌套异常的消息字段吗?
答案 0 :(得分: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);
}
其中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);
}
};
}
}
和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;
}
}
这有点像黑客,你只能在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
}
}