我想处理来自假装客户端的任何异常,即使服务不可用。但是我无法使用try/catch来捕获它们。这是我的假客户端:
@FeignClient(name = "api-service", url ="localhost:8888")
public interface ClientApi extends SomeApi {
}
其中 api 是:
@Path("/")
public interface SomeApi {
@GET
@Path("test")
String getValueFromApi();
}
客户端与 try/catch 的用法:
@Slf4j
@Service
@AllArgsConstructor
public class SampleController implements SomeApi {
@Autowired
private final ClientApi clientApi;
@Override
public String getValueFromApi() {
try {
return clientApi.getValueFromApi();
} catch (Throwable e) {
log.error("CAN'T CATCH");
return "";
}
}
}
依赖项在版本中:
- 弹簧引导 2.2.2.发布
- 春天的云霍克斯顿.SR1
代码应该按照如何管理假装错误?工作。
我收到了一些长堆栈跟踪,其中异常是:
- 原因:java.net.Connect异常:连接被拒绝(连接被拒绝(
- 原因:假装。可重试异常:连接被拒绝(连接被拒绝(执行 GET http://localhost:8888/test
- 原因:com.netflix.hystrix.exception.HystrixRuntimeException:ClientApi#getValueFromApi(( 失败,没有可用的回退。
如何正确捕获Feign exptions,即使客户端服务(在本例中为localhost:8888(不可用?
附言。当假装客户端服务可用时,它可以工作,好的。我只关注例外方面。
处理服务不可用情况的更好方法是使用断路器模式。幸运的是,使用 Netflix Hystrix 作为断路器模式的实现很容易。
首先,您需要在应用程序配置中为假客户端启用 Hystrix。
应用程序.yml
feign:
hystrix:
enabled: true
然后,您应该为指定的假客户端接口编写一个回退类。 在这种情况下getValueFormApi
回退类中的方法将主要类似于您编写catch
块(电路处于打开状态且不会尝试原始方法的情况除外(。
@Component
public class ClientApiFallback implements ClientApi {
@Override
public String getValueFromApi(){
return "Catch from fallback";
}
}
最后,您只需要为假客户端指定回退类。
@FeignClient(name = "api-service", url ="localhost:8888", fallback = ClientApiFallback.class)
public interface ClientApi extends SomeApi {
}
这样,您的方法getValueFromApi
是故障安全的。如果 出于任何原因,任何未捕获的异常都会从getValueFromApi
调用ClientApiFallback
方法进行转义。
若要启用断路器并配置应用程序以处理意外错误,需要:
1.- 使能断路器本身
@SpringBootApplication
@EnableFeignClients("com.perritotutorials.feign.client")
@EnableCircuitBreaker
public class FeignDemoClientApplication {
2.- 创建你的后备 bean
@Slf4j
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PetAdoptionClientFallbackBean implements PetAdoptionClient {
@Setter
private Throwable cause;
@Override
public void savePet(@RequestBody Map<String, ?> pet) {
log.error("You are on fallback interface!!! - ERROR: {}", cause);
}
}
回退实现时必须记住的一些事项:
- 必须标记为@Component,它们在整个应用程序中是唯一的。
- 回退 Bean 应该有一个原型作用域,因为我们希望为每个异常创建一个新的作用域。
- 使用构造函数注入进行测试。
3.- 您的错误解码器,根据返回的 HTTP 错误实现回退启动:
public class MyErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
return new MyCustomBadRequestException();
}
if (response.status() >= 500) {
return new RetryableException();
}
return defaultErrorDecoder.decode(methodKey, response);
}
}
4.- 在你的配置类中,将重试器和错误解码器添加到 Spring 上下文中:
@Bean
public MyErrorDecoder myErrorDecoder() {
return new MyErrorDecoder();
}
@Bean
public Retryer retryer() {
return new Retryer.Default();
}
您还可以向重试程序添加自定义:
class CustomRetryer implements Retryer {
private final int maxAttempts;
private final long backoff;
int attempt;
public CustomRetryer() {
this(2000, 5); //5 times, each 2 seconds
}
public CustomRetryer(long backoff, int maxAttempts) {
this.backoff = backoff;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
public void continueOrPropagate(RetryableException e) {
if (attempt++ >= maxAttempts) {
throw e;
}
try {
Thread.sleep(backoff);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
@Override
public Retryer clone() {
return new CustomRetryer(backoff, maxAttempts);
}
}
如果您想获得有关如何在应用程序中实现 Feign 的功能示例,请阅读本文。