Spring Cloud Hystrix学习笔记
Spring Cloud Hystrix主要保障服务的高可用,它提供服务隔离,以减少不同服务之间资源竞争带来的相互影响;提供服务降级;提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。同时也可以监控服务的调用情况。
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
一、服务降级
服务调用过程中,遇到超时、异常。服务熔断或线程池/信号量打满,执行另一个方法,fallback,返回提示信息。
1.1、服务方
- 在service方法上加上注解:
@HystrixCommand(
// 兜底方法
fallbackMethod = "timeoutHandle",
commandProperties = {
// 超时时间设置,默认1000,单位ms
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "3000"),
// 是否开启超时设置,默认true
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_TIMEOUT_ENABLED, value = "true"),
// 是否开启兜底方法,默认true
@HystrixProperty(name = HystrixPropertiesManager.FALLBACK_ENABLED, value = "true")
}
)
其中fallbackMethod 指定处理服务降级的方法,value为最大时间,单位ms。
public String timeoutHandle() {
return "服务繁忙,请稍后再试-";
}
注:fallback方法必须有主方法的入参。
不一定要超时,方法异常也会走fallback方法,fallback方法可以在入参里加上异常,对异常进行处理。
public String timeoutHandle(Expection e) {
if (e != null) {
return "方法异常";
}
return "服务繁忙,请稍后再试-";
}
- 启动类加上注解
@EnableCircuitBreaker
1.2、消费方
- 在配置文件开启feign的hystrix:
feign:
hystrix:
enabled: true
- 是在controller方法上加上注解:
@HystrixCommand(
// 兜底方法
fallbackMethod = "timeoutHandle",
commandProperties = {
// 超时时间设置,默认1000,单位ms
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "3000"),
// 是否开启超时设置,默认true
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_TIMEOUT_ENABLED, value = "true"),
// 是否开启兜底方法,默认true
@HystrixProperty(name = HystrixPropertiesManager.FALLBACK_ENABLED, value = "true")
}
)
- 启动类加上注解
@EnableHystrix
注意:服务方与消费方在启动类上加的开启注解不同
1.3、全局服务降级
一次性对多个做相同的服务降级处理
1.3.1、第一种方式
- 在类上加注解
@DefaultProperties(defaultFallback = "orderGlobalFallbackMethod")
其中orderGlobalFallbackMethod为处理服务降级的方法。
- 在需要按全局服务降级方法处理的方法上加上注解
@HystrixCommand
1.3.2、第二种方式
适用于feign调用的消费方
- 实现注解
@FeignClient
标注的接口,在实现方法时返回服务降级处理的提示
@Component
public class HystrixPaymentFeignServiceImpl implements HystrixPaymentFeignService {
@Override
public String timeout(int id) {
return "系统繁忙,请稍后再试";
}
}
注意:记得在实现类上加上@Component
- 在@FeignClient注解里面加上 fallback = xxx.class 指向实现类
@Component
@FeignClient(value = "HYSTRIX-PAYMENT-SERVICE", fallback = HystrixPaymentFeignServiceImpl.class)
public interface HystrixPaymentFeignService {
/**
* 测试feign的超时控制
* @return 提示
*/
@GetMapping("/payment/timeout/{id}")
String timeout(@PathVariable("id") int id);
}
二、服务熔断
服务熔断在服务方做
原理:
默认情况下,如果统计时间达到(metrics.rollingStats.timeInMilliseconds)10,000ms,请求次数达到(circuitBreaker.requestVolumeThreshold)20次或以上,请求失败率达到或超过(circuitBreaker.errorThresholdPercentage)50%,断路器就会开启,服务熔断,拒绝请求,返回回退方法的结果,持续时间(circuitBreaker.sleepWindowInMilliseconds)5,000ms,5,000ms之后,断路器进入半开状态,尝试接收请求,如果成功,断路器关闭,如果失败,断路器开启,5,000ms之后继续尝试。
2.1、使用方法
1、在需要开启服务熔断的service方法上加上注解,其中是否使用线程池隔离,统计时间和桶的数量的设置位置不一样,使用线程池隔离的在 threadPoolProperties 里面设置
没有使用线程池隔离
@HystrixCommand(
// 回退方法
fallbackMethod = "paymentCircuitBreakerFallback",
commandProperties = {
// 是否开启断路器,默认true
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED, value = "true"),
// 服务熔断的最小请求次数,默认20
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "20"),
// 断路器开启的请求错误率百分比,默认50,即失败率达到一半或以上
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
// 服务熔断时间,默认5000ms
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000"),
// 是否强制开启断路器,默认false
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_FORCE_OPEN, value = "false"),
// 是否强制关闭断路器,默认false
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_FORCE_CLOSED, value = "false"),
// 统计时间,默认10,000ms
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_STATS_TIME_IN_MILLISECONDS, value = "10000"),
// 桶的数量,默认10个,用来记录请求结果状态(成功、失败、超时、拒绝),必须被统计时间整除,建议数百到数千毫秒一个桶
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_STATS_NUM_BUCKETS, value = "10"),
// 统计结果(请求数、失败率等)刷新间隔时间,默认500ms
@HystrixProperty(name = HystrixPropertiesManager.METRICS_HEALTH_SNAPSHOT_INTERVAL_IN_MILLISECONDS, value = "500")
}
)
使用线程池隔离
@HystrixCommand(
// 回退方法
fallbackMethod = "paymentCircuitBreakerFallback",
commandProperties = {
// 服务隔离设置,默认线程池隔离 THREAD,信号量 SEMAPHORE
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "THREAD"),
// 是否开启断路器,默认true
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED, value = "true"),
// 服务熔断的最小请求次数,默认20
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "20"),
// 断路器开启的请求错误率百分比,默认50,即失败率达到一半或以上
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
// 服务熔断时间,默认5000ms
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000"),
// 是否强制开启断路器,默认false
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_FORCE_OPEN, value = "false"),
// 是否强制关闭断路器,默认false
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_FORCE_CLOSED, value = "false"),
// 统计时间,默认10,000ms
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_STATS_TIME_IN_MILLISECONDS, value = "10000"),
// 桶的数量,默认10个,用来记录请求结果状态(成功、失败、超时、拒绝),必须被统计时间整除,建议数百到数千毫秒一个桶
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_STATS_NUM_BUCKETS, value = "10"),
// 统计结果(请求数、失败率等)刷新间隔时间,默认500ms
@HystrixProperty(name = HystrixPropertiesManager.METRICS_HEALTH_SNAPSHOT_INTERVAL_IN_MILLISECONDS, value = "500")
},
// commandKey = "order-testThread",
// 一组hystrix命令,默认类名
// groupKey = "order-OrderController",
// 线程池名称,默认取(hystrix-) + (groupKey) ,即默认同一个类中使用一个线程池
// threadPoolKey = "order-pool-1",
threadPoolProperties = {
// 核心线程数,默认10
@HystrixProperty(name = HystrixPropertiesManager.CORE_SIZE, value = "10"),
// 空闲线程最大存活时间,默认为 2,即两分钟
@HystrixProperty(name = HystrixPropertiesManager.KEEP_ALIVE_TIME_MINUTES, value = "2"),
// 队列的最大值,默认值为 -1
@HystrixProperty(name = HystrixPropertiesManager.MAX_QUEUE_SIZE, value = "10"),
// 执行拒绝策略的队列大小,默认5,所以当队列最大值大于5时,一般要设置此项
@HystrixProperty(name = HystrixPropertiesManager.QUEUE_SIZE_REJECTION_THRESHOLD, value = "10"),
// 统计时间,默认10,000ms
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_STATS_TIME_IN_MILLISECONDS, value = "10000"),
// 桶的数量,默认10个,用来记录请求结果状态(成功、失败、超时、拒绝),必须被统计时间整除,建议数百到数千毫秒一个桶
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_STATS_NUM_BUCKETS, value = "10")0
}
)
2、启动类加上注解:
@EnableCircuitBreaker
注:如果失败率达到但是“请求次数”没达到,将不会开启服务熔断机制。
2.2、学习中的问题
2.2.1、metrics.rollingStats.numBuckets存在的意义?
-
buckets的作用:
buckets是用来存放接口请求的结果的状态,包括成功、失败、超时、拒绝。 -
统计数据的实现:
默认情况下(即10s10个桶),一个桶记录1s内接口的请求结果状态,进入下一秒后,就会新增一个桶,如果桶的数量超过10个,就会去掉最前面的一个桶,断路器是否开启的依据就是取最后10个桶的综合结果,(metrics.rollingStats.timeInMilliseconds / metrics.rollingStats.numBuckets)就是新增一个桶的间隔时间,所以这个值必须是整数。
三、服务监控
3.1、创建监控服务
- 创建项目;
- 引入依赖;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- 配置监控参数
在需要监控的接口上加上注解
@HystrixCommand(
commandProperties = {
// 是否开启百分位的统计,默认true
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_PERCENTILE_ENABLED, value = "true"),
// 百分位统计时间区间,默认60,000ms
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_PERCENTILE_TIME_IN_MILLISECONDS, value = "60000"),
// 百分位统计的桶的数量,默认6个,桶的数量必须能被统计时间整除
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_PERCENTILE_NUM_BUCKETS, value = "6"),
// 每个桶统计的请求最大个数,默认100个,超过就取最后的
@HystrixProperty(name = HystrixPropertiesManager.METRICS_ROLLING_PERCENTILE_BUCKET_SIZE, value = "100")
}
)
- 开启监控
在启动类加上注解
@EnableHystrixDashboard
- 访问页面:http://localhost:9001/hystrix
监控路径为需要监控的服务IP和端口加上/hystrix.stream
http://localhost:8001/hystrix.stream
3.2、问题解决
- 如果报错Unable to connect to Command Metric Stream.在需要监控的服务启动类加上:
@Bean
public ServletRegistrationBean<HystrixMetricsStreamServlet> getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
其中registrationBean.addUrlMappings("/hystrix.stream");为指定监控路径。
2. 需要监控的服务是否有引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
四、服务隔离(限流)
Tomcat默认只有一个线程池,所有http请求共享,当服务中部分接口响应过慢,导致请求一直占用线程,最后请求堆积到占满这个线程池,就没有线程可以接收新的请求,致使服务中其他接口也无法访问,最终导致整个服务不可用,这就是服务的雪崩效应,服务隔离就是为了防止服务的雪崩,保护服务。
缺点:增加CPU和内存开销
Hystrix的服务隔离策略有两种,分别为:线程池隔离和信号量隔离。
4.1、线程池隔离
- 引入依赖;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 在方法上加上注解;
@HystrixCommand(
commandProperties = {
// 服务隔离设置,默认线程池隔离 THREAD,信号量 SEMAPHORE
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "THREAD"),
// 线程隔离中超时是否中断执行,默认true
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_INTERRUPT_ON_TIMEOUT, value = "true"),
// 同时有多少个线程可以调用兜底方法,默认10,超出就会报错,在服务隔离时设置使用
@HystrixProperty(name = HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "10")
},
// commandKey = "order-testThread",
// 一组hystrix命令,默认类名
// groupKey = "order-OrderController",
// 线程池名称,默认取(hystrix-) + (groupKey) ,即默认同一个类中使用一个线程池
// threadPoolKey = "order-pool-1",
threadPoolProperties = {
// 核心线程数,默认10
@HystrixProperty(name = HystrixPropertiesManager.CORE_SIZE, value = "10"),
// 空闲线程最大存活时间,默认为 2,即两分钟
@HystrixProperty(name = HystrixPropertiesManager.KEEP_ALIVE_TIME_MINUTES, value = "2"),
// 队列的最大值,默认值为 -1
@HystrixProperty(name = HystrixPropertiesManager.MAX_QUEUE_SIZE, value = "10"),
// 执行拒绝策略的队列大小,默认5,所以当队列最大值大于5时,一般要设置此项
@HystrixProperty(name = HystrixPropertiesManager.QUEUE_SIZE_REJECTION_THRESHOLD, value = "10")
},
// 异常情况的兜底方法
fallbackMethod = "fallbackMethod"
)
- 在启动类上加上注解开启hystrix;
@EnableHystrix
4.2、信号量隔离
就是对接口进行限流,只是增加了一个计数器,计算接口占用的请求线程数量是否达到最大值,每一次请求计数器加一,每一次响应计数器减一。
- 引入依赖;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 在方法上加上注解;
@HystrixCommand(
// 兜底方法
fallbackMethod = "fallbackMethod",
commandProperties = {
// 服务隔离设置,默认线程池隔离 THREAD,信号量 SEMAPHORE
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
// 最大并发请求数,即信号量的大小,默认10
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "10"),
// 同时有多少个线程可以调用兜底方法,默认10,超出就会报错,在服务隔离时设置使用
@HystrixProperty(name = HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "10")
}
)
- 在启动类上加上注解开启hystrix;
@EnableHystrix
4.3、两种隔离的区别
区别项 | 线程池隔离 | 信号量隔离 |
---|---|---|
是否创建线程池 | ✔ | ✘ |
是否支持排队 | ✔ | ✘ |
增加的开销大小 | 大 | 小 |
从接口耗时方面适合使用的场景 | 长 | 短 |