Hystrix简介
Hystrix主要完成了下面的功用:
包裹恳求:运用 HystrixCommand(或 HystrixObservableCommand) 包裹对依赖的调用逻辑。每个命令在独立的线程中执行,运用了设计形式中的‘命令形式’
跳闸机制:当某微效劳的错误率超越一定阈值时,能够自动跳闸,中止恳求该效劳一段时间
资源隔离:Hystrix 为每个微效劳都维护了一个小型的线程池(或信号量)假如该线程池已满,发往该依赖的恳求就会被立刻回绝
监控:Hystrix 能够近乎实时的监控运转指标和配置的变化,例如胜利、失败、超时和被回绝的恳求等
回退机制:当恳求胜利、失败、超时和被回绝或者断路器翻开时,执行回退逻辑。回退逻辑可由开发人员自行提供
自我修复:断路器翻开一段时间后,会进入‘半开’状态,允许一个恳求访问效劳提供方,假如胜利。则关闭断路器
运用 Hystrix
引入依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
在启动类上添加 @EnableHystrix
两种状况下的回退办法
非 Feign 调用下的回退办法
编写回退办法
/**
需求留意:
回退办法的返回值类型需求和原来办法返回值类型相同(否则会报 FallbackDefinitionException: Incompatible return types)
回退办法的参数列表也要和原来办法相同(否则会报 FallbackDefinitionException: fallback method wasn’t found: getUserByAgeFallBack([class java.lang.Integer]))
当我写下第二句时,发现书中下一节引见说能够经过在回退办法中添加第二个参数:ThrowEable 来捕获异常,剖析调用失败的缘由,我就晓得我错了。为了防止继续得到错误的结论,我决议读一读 Hystrix 处置回退办法的源码
加点料:Hystrix 对回退办法的封装的源码如下:
com.netflix.hystrix.contrib.javanica.utils.MethodProvider
public FallbackMethod find(Class> enclosingType, Method commandMethod, boolean extended) {
// 首先判别该办法的 HystrixCommand 注解上有没有 defaultFallback / fallbackMethod 配置回退办法称号
if (this.canHandle(enclosingType, commandMethod)) {
// 调用 doFind 办法
return this.doFind(enclosingType, commandMethod, extended);
} else {
// 没有配置的化就接着下一个判别
return this.next != null ? this.next.find(enclosingType, commandMethod, extended) : FallbackMethod.ABSENT;
}
}
find 办法在用户所恳求的办法的 HystrixCommand 注解上有用 defaultFallback / fallbackMethod 配置回退办法称号的时分,会调用 doFind 办法来寻觅回退办法。该办法的参数有两个,enclosingType 是用户所恳求的办法的类字节码文件,commandMethod 是用户所恳求的办法
首先经过 this.getFallbackName 获取回退办法称号,接着经过获取 commandMethod 的参数类型们
接着分两种状况:
回调办法继承于 commandMethod 且最后一个参数类型是 Throwable,则去掉回退办法参数列表中的 Throwable 类型停止匹配
回调办法不继承于 commandMethod ,则存在两个可能的参数类型列表: fallbackParameterTypes 和 extendedFallbackParameterTypes 前者是 commandMethod 是参数列表,后者是前者 + Throwable。然后两个都停止匹配。接着运用 Java8 Optional API,按次第选取前者匹配到的办法 / 后者 / 空返回
private FallbackMethod doFind(Class> enclosingType, Method commandMethod, boolean extended) {
String name = this.getFallbackName(enclosingType, commandMethod);
Class>[] fallbackParameterTypes = null;
if (this.isDefault()) {
fallbackParameterTypes = new Class[0];
} else {
fallbackParameterTypes = commandMethod.getParameterTypes();
}
if (extended && fallbackParameterTypes[fallbackParameterTypes.length - 1] == Throwable.class) {
fallbackParameterTypes = (Class[])ArrayUtils.remove(fallbackParameterTypes, fallbackParameterTypes.length - 1);
}
Class>[] extendedFallbackParameterTypes = (Class[])Arrays.copyOf(fallbackParameterTypes, fallbackParameterTypes.length + 1);
extendedFallbackParameterTypes[fallbackParameterTypes.length] = Throwable.class;
Optional exFallbackMethod = MethodProvider.getMethod(enclosingType, name, extendedFallbackParameterTypes);
Optional fMethod = MethodProvider.getMethod(enclosingType, name, fallbackParameterTypes);
Method method = (Method)exFallbackMethod.or(fMethod).orNull();
if (method == null) {
throw new FallbackDefinitionException("fallback method wasn’t found: " + name + “(” + Arrays.toString(fallbackParameterTypes) + “)”);
} else {
return new FallbackMethod(method, exFallbackMethod.isPresent(), this.isDefault());
}
}
由源码能够得到结论:回退办法要么参数列表和原始办法相同,要么加且仅加一个类型为 Throwable 的参数。其他的都不行
Feign 客户端下的回退办法
设置:feign.hystrix.enabled: true
Feign 客户端接口上的 @FeignClient 添加 fallback 属性,指向回退类
回退类完成客户端接口
feign:
hystrix:
enabled: true # 翻开 feign 的 hystrix 支持
留意回退类加上 @Component 接口,防止由于 Spring 容器找不到该类而启动报错
// Feign 客户端接口上的 @FeignClient 添加 fallback 属性,指向回退类
@FeignClient(name = “SERVICE-PROVIDER”, fallback = UserServiceFeignClientFallBack.class)
public interface UserServiceFeignClient {
@GetMapping("/api/v1/user/{age}")
User getUser(@PathVariable(“age”) Integer age);
/**
* 用户列表
* @return
*/
@GetMapping("/api/v1/users")
List getUsers();
}
// 回退类完成客户端接口
@Component
public class UserServiceFeignClientFallBack implements UserServiceFeignClient {
@Override
public User getUser(Integer age) {
return null;
}
@Override
public List getUsers() {
return null;
}
}
当采用 Feign 客户端来完成回退的时分,前面的捕捉异常办法就不起作用了,那我们应该如何来处置异常呢?能够运用 @FeignClient 的 fallbackFactory 属性
@FeignClient(name = “SERVICE-PROVIDER”, fallbackFactory = UserServiceFallbackFactory.class)
@Component
@Slf4j
public class UserServiceFallbackFactory implements FallbackFactory {
@Override
public UserServiceFeignClient create(Throwable t) {
// 日志最好写在各个 fallback 办法中,而不要直接卸载 create办法中
// 否则援用启动时就会打印该日志
return new UserServiceFeignClient() {
@Override
public User getUser(Integer age) {
log.info(“调用User效劳提供者失败”, t);
User user = new User();
user.setName(“默许用户”);
user.setAge(age);
return user;
}
@Override
public List getUsers() {
return null;
}
};
}
}
留意: **fallback 和 fallbackFactory 属性同时存在时,fallback 的优先级更高。因而开发中假如需求处置异常,只需配置 fallbackFactory 属性即可 **
防止业务异常走进回退办法
在某些场景下,当发作业务异常时,我们并不想触发 fallback。例如业务中判别年龄 age 不能小于 1,否则抛出异常
if(age < 1){
throw new KeatsException(ExceptionEnum.NUM_LESS_THAN_MIN);
}
这时 Hystrix 会捕捉到异常然后执行 fallback 办法,我们能够经过下面两个办法来防止:
继承 HystrixBadRequestException 该类继承自 RunntimeException
在 @HystrixCommand 添加属性 ignoreExceptions = {KeatsException.class}
为 Feign 禁用 Hystrix
只需翻开 feign 的 hystrix 支持开关,feign 就会运用断路器包裹 feign 客户端的一切办法,但很多场景并不需求这样。该如何禁用呢?
为指定客户端禁用。需求借助 Feign 的自定义配置。首先添加一个自定义配置类,然后配置到 @FeignClient 的 configuration 属性中
@Configuration
public class FeignDisableHystrixConfiguration {
@Bean
@Scope(“prototype”)
public Feign.Builder feignBuilder(){
return Feign.builder();
}
}
@FeignClient(name = “SERVICE-PROVIDER”, configuration = {FeignDisableHystrixConfiguration.class})
全局禁用: feign.hystrix.enabled: false