微服务中使用 Feign 实现服务间通信,Hystrix 提供服务间容错。本文主要讲hystrix中的 超时,熔断 ,隔离策略,适合对Hystrix 功能有基本了解的读者。
1 准备工作
三个服务: 服务提供者provider,服务消费者consumer,注册中心
provider 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
provider 配置文件
server.port=9091
spring.application.name=provider-service
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9090/eureka/
consumer 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
consumer 配置
server.port=9092
spring.application.name=consumer-service
eureka.client.serviceUrl.defaultZone=http://132.232.40.60:9090/eureka/
# 开启hystrix
feign.hystrix.enabled=true
consumer 定义服务端的Feign接口
@FeignClient(value = "provider-service", fallbackFactory = OrderClientFallback.class)
public interface OrderClient {
@GetMapping("/hystrix/timeOut")
String getOrder();
@GetMapping("/hystrix/strategy/thread")
String strategyThread();
@GetMapping("/hystrix/strategy/semaphore")
String strategySemaphore();
@GetMapping("/hystrix/circuitBreaker")
String circuitBreake(@RequestParam("n") int n, @RequestParam("m") int m);
}
OrderClientFallback:
@Component
public class OrderClientFallback implements FallbackFactory<OrderClient>{
@Override
public OrderClient create(Throwable cause) {
return new OrderClient(){
@Override
public String getOrder() {
return "被降降级了";
}
@Override
public String strategyThread() {
return "被降降级了";
}
@Override
public String circuitBreake(int n,int m) {
if (cause != null) {
cause.printStackTrace();
}
return "被降降级了";
}
@Override
public String strategySemaphore() {
if (cause != null) {
cause.printStackTrace();
}
return "被降降级了";
}
};
}
}
注册中心 省略
2 实战
在Hystrix 中配置文件主要放在 HystrixCommandProperties.java 文件中。
1) 超时: 默认开启,超时时间默认设置为1秒。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cLRXQXOJ-1615456342143)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1614845569106.png)]
服务端设置超时两秒
@GetMapping("/hystrix/timeOut")
public String timeOut() throws InterruptedException {
Thread.sleep(2000);
return "ok";
}
请求测试:超时后服务被降级,走了Fallback方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-65Jl7uXn-1615456342146)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1614846267088.png)]
修改consumer配置或者设置实例属性:超时时间设置为5秒,再次请求。
- 默认属性配置
##默认属性
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
##实例属性
## hystrix.command.HystrixCommandkey.execution.isolation.thread.timeoutInMilliseconds=500
-
实例属性配置
@GetMapping("/test/timeOut") @HystrixCommand(commandProperties = { @HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds",value = "5000") }) public String timeOut(){ String str = orderClient.timeOut(); return str; }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pa8agnHI-1615456342148)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1614849949077.png)]
2)断路器:默认开启,采用滑动窗口算法,窗口默认为10个,每个存储时间为1秒。请求阈值默认20,失败率超过50%开启断路器,断路器默认休眠5s。
解释:在10秒内必须有20个请求通过,失败率在50% 会触发开关,开关打开后在5秒被不会再请求服务端,5s后尝试请求判断是否成功,失败继续断开,成功关闭断路器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NvyZilMt-1615456342151)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1614851042701.png)]
利用jmerer和伪代码测试断路器。
服务端伪代码 当传入参数n>15 m<=60 程序会报错(制造错误率)
@GetMapping("/hystrix/circuitBreaker")
public String circuitBreaker(int n,int m){
if(n>15&&m<=60){
int a=0;
int b=10;
int c=b/a;
}
return n+"";
}
jemter 设置30 秒通过100个线程。超过默认10秒20线程,当失败率达到50% 短路器打开。从图中可以看出断路器开启了5s。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYKHm1a7-1615456342153)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1614853363572.png)]
修改断路器默认配合属性:
- 默认默认属性修改
# 请求阈值
hystrix.command.default.circuitBreaker.requestVolumeThreshold=10
# 失败率
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
## 断路器打开后休眠时间,默认5秒
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=2000
-
实例属性修改
@HystrixCommand(commandProperties = { @HystrixProperty(name ="circuitBreaker.requestVolumeThreshold",value = "10"), @HystrixProperty(name ="circuitBreaker.errorThresholdPercentage",value = "50"), @HystrixProperty(name ="circuitBreaker.sleepWindowInMilliseconds",value = "2000"), })
3) 隔离策略:线程池+信号量
-
线程池(Threadpoll)
默认使用的是线程池隔离模式。
线程池默认配置属性: HystrixThreadPoolProperties.java
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HabevZdo-1615456342154)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1615448069394.png)]
-
属性修改:
-
1 修改默认属性
#默认属性 hystrix.command.default.execution.isolation.strategy=THREAD ##使用 maximumSize 必须开启allowMaximumSizeToDivergeFromCoreSize=true hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize=true hystrix.threadpool.default.coreSize=2 hystrix.threadpool.default.maximumSize=2 hystrix.threadpool.default.keepAliveTimeMinutes=2 hystrix.threadpool.default.maxQueueSize=10 hystrix.threadpool.default.queueSizeRejectionThreshold=2 #实例属性 HystrixtTreadpoolKey 默认类名 hystrix.threadpool.HystrixtTreadpoolKey.coreSize=2
使用maximumSize必须开启allowMaximumSizeToDivergeFromCoreSize
public Integer actualMaximumSize() { final int coreSize = coreSize().get(); final int maximumSize = maximumSize().get(); if (getAllowMaximumSizeToDivergeFromCoreSize().get()) { if (coreSize > maximumSize) { return coreSize; } else { return maximumSize; } } else { return coreSize; } }
-
2 修改实例属性:
- 注解方式:
@HystrixCommand( groupKey = "UserController",//默认类名 commandKey = "strategyThread",//默认方法名 threadPoolKey = "provider-service",//服务提供者名 threadPoolProperties = { @HystrixProperty(name = "coreSize",value = "2"), @HystrixProperty(name = "maxQueueSize",value = "10"), @HystrixProperty(name = "keepAliveTimeMinutes",value = "2"), @HystrixProperty(name = "queueSizeRejectionThreshold",value = "10") },fallbackMethod = "fallback")
注意:注解方式使用设置不了 maximumSize,(HystrixPropertiesManager.java)中没有定义
maximumSize 属性。
-
测试结果。
线程池触发拒绝策略抛出异常,触发fallback,当失败率满足熔断器设定的规则,会打开断路器。
-
-
-
-
信号量 (SEMAPHORE)
信号量默认配置:默认允许最大请求数为10
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMQGAk9O-1615456342155)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1615453481891.png)]
- 默认属性修改:
# 信号量 默认属性 hystrix.command.default.execution.isolation.strategy=SEMAPHORE hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=5 # 实例属性 HystrixtCommandKey=提供服务feginClient的服务名#方法名(参数类型) 默认是方法名 # 例如 OrderClient#strategySemaphore(int) hystrix.command.HystrixtCommandKey.execution.isolation.semaphore.maxConcurrentRequests=5
- 注解方式:fallback 方法和实例方法在同类并且参数必须一致。
@HystrixCommand(commandProperties={ @HystrixProperty(name = "execution.isolation.strategy",value = "SEMAPHORE"), @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "5") },fallbackMethod = "fallback")
-
测试结果
当线程超过设定值,触发fallback ,满足熔断条件,开启断路器。
3 总结:
hystrix 降级策略 分为三种:
- 超时降级
- 默认开启 (timeOut=1s)
- 熔断降级(特殊的降级方式)
- 默认开启 (10秒内20个请求 失败率在50%)
- 隔离策略降级
- 线程池隔离 默认使用策略
- 信号量隔离