1. Hystrix 简介
- 熔断机制
熔断机制是服务雪崩的一种有效解决方案。当指定时间窗内的请求失败率达到设定阈值
时,系统将通过断路器直接将此请求链路断开。常见的熔断有两种:预熔断 和 即时熔断
- 服务降级
服务降级是请求发生问题后的一种增强用户体验的方式。
现代系统中,发生了服务熔断,一定会出现服务降级;发生了服务降级,不一定会发生
服务熔断。
Spring Cloud 是通过 Hystrix 来实现服务熔断与降级的。
Hystrix 是一种开关装置,类似于熔断保险丝。在消费者端安装一个 Hystrix 熔断器,当
Hystrix 监控到某个服务发生故障后熔断器会开启,将此服务访问链路断开。不过 Hystrix 并不会将该服务的消费者阻塞,或向消费者抛出异常,而是向消费者返回一个符合预期的备选响应(FallBack)。
通过 Hystrix 的熔断与降级功能,避免了服务雪崩的发生,同时也考虑到了
用户体验。故 Hystrix 是系统的一种防御机制。
2.服务降级
Hystrix 对于服务降级的实现方式有3种:fallbackMethod, fallbackFactory 和 fallback
- 添加pom依赖
<!--hystrix 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 在启动类添加注解@EnableCircuitBreaker
一般可以直接使用 @SpringCloudApplication 代替下面三个注解:
@SpringBootApplication 服务启动类
@EnableDiscoveryClient 服务注册发现
@EnableCircuitBreaker 开启服务熔断降级
2.1 fallbackMethod 服务降级
@HystrixCommand(fallbackMethod = "getHandleback")
@GetMapping("/get/{id}")
public Depart getHandle(@PathVariable("id") int id) {
return departService.getDepartById(id);
}
public Depart getHandleback(@PathVariable("id") int id) {
return null;
}
2.2 fallbackFactory 服务降级
/**
* 服务降级类,实现了FallbackFactory接口,该接口的泛型为Feign接口
*/
@Component
public class DepartFallbackFactory implements FallbackFactory<DepartService> {
@Override
public DepartService create(Throwable throwable) {
return new DepartService() {
@Override
public List<Depart> listAllDeparts() {
System.out.println("执行listAllDeparts()的服务降级处理方法");
return null;
}
};
}
}
@Service
// fallbackFactory属性指定服务降级类
@FeignClient(value = "provider-depart", fallbackFactory = DepartFallbackFactory.class)
@RequestMapping("/provider/depart")
public interface DepartService {
@GetMapping("/list")
List<Depart> listAllDeparts();
}
2.3 fallback 服务降级
/**
* 服务降级类,实现了Feign接口
* 该类上要添加一个@RequestMapping,其值为对应的处理器类上的@RequestMapping的值前添加 /fallback
*/
@Component
@RequestMapping("/fallback/consumer/depart")
public class DepartFallback implements DepartService {
@Override
public List<Depart> listAllDeparts() {
System.out.println("执行listAllDeparts()的服务降级处理方法");
return null;
}
}
@Service
// fallback属性指定服务降级类
@FeignClient(value = "provider-depart", fallback = DepartFallback.class)
@RequestMapping("/provider/depart")
public interface DepartService {
@GetMapping("/list")
List<Depart> listAllDeparts();
}
3.隔离策略
3.1 隔离类型
- 线程隔离 thread:
Hystrix 的默认隔离策略。系统会创建一个依赖线程池,为每个依赖请
求分配一个独立的线程,而每个依赖所拥有的线程数量是有上限的。当对该依赖的调用
请求数量达到上限后再有请求,则该请求阻塞。所以对某依赖的并发量取决于为该依赖
所分配的线程数量。 - 信号量隔离:
对依赖的调用所使用的线程仍为请求线程,即不会为依赖请求再新创建新
的线程。但系统会为每种依赖分配一定数量的信号量,而每个依赖请求分配一个信号号。
当对该依赖的调用请求数量达到上限后再有请求,则该请求阻塞。所以对某依赖的并发
量取决于为该依赖所分配的信号数量。
3.2 修改策略
- 线程是进程的一个执行体,其可独立运行,一个主机中的线程数量是有上限的。
- 信号量是一个计数器的值,所以其不具有执行特性,信号量就是个计数器,不存在上限
的概念。 - 线程隔离是对依赖的调用,使用了两个线程;信号量隔离中对依赖的调用,仅使用了用
户请求线程。故,在复杂系统的长链路调用中,线程隔离的执行效率要高于信号量隔离
的(干活的多了)。 - 在互联网项目中使用线程隔离,在企业内部系统中使用信号量隔离。
//默认的线程隔离策略
hystrix.command.default.execution.isolation.strategy=thread
//默认开启线程的执行时间超时时限
hystrix.command.default.execution.timeout.enabled=true
//线程执行时限,默认1秒,开启超时时限生效
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
//当线程执行超时时是否中断线程的执行,默认为 true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true
//在线程执行过程中,若请求取消了,当前执行线程是否结束,默认为false
hystrix.command.default.execution.isolation.thread.interruptOnCancel=false
//信号量隔离
hystrix.command.default.execution.isolation.strategy=semaphore
//信号量的数量,即对某一依赖所允许的请求的最高并发量
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests
4 服务熔断配置
- 开启熔断器是指将拒绝所有请求;关闭熔断器是指将使所有请求通过
//设置当前应用是否开启熔断器功能,默认值为 true
hystrix.command.default.circuitBreaker.enabled=true
//当在时间窗内(10 秒)收到的请求数量超过该设置的数量后,将开启熔断器。默认值为 20
hystrix.command.default.circuitBreaker.requestVolumeThreshold=20
//当熔断器开启该属性设置的时长后,会尝试关闭熔断器,以恢复被熔断的服务。默认值为 5000 毫秒。
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
//当请求的错误率高于该百分比时,开启熔断器。默认值为 50,即 50%。
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
//设置熔断器无需条件开启,拒绝所有请求。默认值为 false
hystrix.command.default.circuitBreaker.forceOpen=false
//设置熔断器无需条件的关闭,通过所有请求。默认值为 false
hystrix.command.default.circuitBreaker.forceClosed=false
5. 线程池相关属性
关于执行线程的线程池,可以通过以下的这些属性设置