Hystrix–断路器
1、什么是Hystrix
服务雪崩:
对于一个分布式系统来讲,随着服务的数量越来越多,服务与服务之间的调用链路也会越来越长,不可避免地在某一个时刻某一个服务变得不可用了,进而会使得链路中所有的服务从调用链的下游开始向上逐级崩溃,甚至导致整个系统中所有的服务崩溃,这就是服务雪崩。
如下面的例子:
服务A调用服务B,服务B又调用服务C,某一时刻A的流量突然增加!在这种情况下,A可能扛得住这些并发,B可能也扛得住,但是C可能扛不住,变得不可用,从而造成短时间内线程的大量阻塞,级联地,B和A的线程也会大量阻塞,从而导致B和A也变得不可用。极端情况下,如果有其他的链路调用了A、B、C,则其他的链路也会逐级崩溃,最终导致整个系统中所有的服务崩溃。像这种一个服务不可用,进而导致整条链路甚至整个系统的服务都不可用的情形,我们称之为服务雪崩!
对于一个分布式系统来讲,某一时刻某一服务变得不可用是一件无法避免的事情,为了防止服务雪崩的产生,我们必须要提供一种机制来维持整个系统的高可用。
而Hystrix就是这样一门技术!它也是由NetFlix公司出品的,但是目前已经进入了停更维护状态。
Hystrix原意“豪猪”,在整个分布式系统中的作用类似于一个“断路器”,当某一个服务单元发生故障不可用后,通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在整个系统中蔓延,最终导致服务雪崩。
简单来讲,就是通过Hystrix及时切断出问题的服务单元,防止崩溃蔓延。
2、Hystrix的功能
通过hystrix可以解决雪崩效应问题,它提供了资源隔离、服务降级、服务融断、缓存、请求合并等功能。
资源隔离:在同一个web容器内,通过某个策略,将执行各个服务逻辑的线程隔离开来,防止某一项服务出现问题后,耗尽web容器的所有资源,进而导致其他的服务也变得不可用,最终导致一个web容器中的服务产生雪崩。Hystrix提供了线程池隔离策略和信号量隔离策略。
服务限流:利用一些方式,限制对核心服务提供者的访问流量,将大流量拦截在核心服务之外,这样可以更好的保证核心服务提供者不出问题。常用的有线程池+队列的方式限流、信号量限流。服务限流和资源隔离是同一过程的不同表现。
服务降级:可分为超时降级、资源(线程或信号量)不足时降级、运行异常降级、熔断降级,降级后返回托底数据。
服务熔断:当请求失败率达到阈值时自动触发降级(如因网络故障或者超时造成的失败率高),熔断器触发的快速失败会进行快速恢复。
缓存:返回结果缓存,后续请求可以直接走缓存。
请求合并:可以实现将一段时间的请求(一般是对同一个接口的请求)合并,然后只对服务提供者发送一次请求。
3、Hystrix的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
要注意的是,Hystrix经常是和Feign一起使用的,而Feign已经集成了Hystrix的一部分功能,所以想要使用Hystrix,可以使用Feign整合方式,此时只需要Feign的依赖即可;也可以使用Hystrix原生注解方式,此时需要导入上面的Hystrix依赖。
后面的服务降级和服务熔断都是针对两种方式的使用作的讲解。
4、Hystrix资源隔离(服务限流)
相关概念:
HystrixCommandKey:在Hystrix中,每一个HystrixCommand都有一个唯一的标识,是必须项,如果不配置,默认值是@HystrixCommand标注的方法名,即每个方法会被当做一个HystrixCommand。
注意:大部分的Hystrix配置都是和HystrixCommandKey绑定的。
HystrixCommandGroupKey:在Hystrix中,将所有的HystrixCommand分组,每一个组的唯一标识叫做GroupKey。分组之后便于统计展示于仪表盘、上传报告和预警等等,也就是说,HystrixCommandGroupKey是Hystrix内部进行度量统计时候的分组标识,数据上报和统计的最小维度就是分组的KEY 。必须项,如果不进行配置,默认值是使用@HystrixCommand标注的方法所在的类名 。
HystrixThreadPoolKey:在Hystrix中,每一组Hystrix命令的执行都有一个独立的线程池,此线程池的唯一标识就叫做HystrixThreadPoolKey,必须项,如果不进行配置,会使用默认使用HystrixCommandGroupKey。
即: 一般来说,command group是对应一个服务,多个command key对应该服务的多个接口, 在默认情况下,每一组Command共享一个线程池。
Semaphore(信号量): 是一个并发工具类,用来控制可同时并发的线程数,其内部维护了一组虚拟许可,通过构造器指定许可的数量,每次线程执行操作时先通过acquire方法获得许可,执行完毕再通过release方法释放许可。如果无可用许可,那么acquire方法将一直阻塞,直到其它线程释放许可。 说白了,它就是一个计数器,我们,我们可以设置一个最大信号量,来控制并发线程的数量,达到限流的目的。
ThreadPool(线程池): 用来控制实际工作的线程数量,通过线程复用的方式来减小内存开销。线程池可同时工作的线程数量是一定的,超过该数量的线程需进入线程队列等待,直到有可用的工作线程来执行任务。
线程池和信号量区别和联系:
1、实际工作的线程是谁创建的,使用线程池,由线程池创建;使用信号量,由你自己手动创建,Semaphore只帮你发放许可。
2、都能够通过控制线程的个数达到限流的目的,设置好允许的最大线程数即可。
资源隔离的核心思想:
在同一个web容器内,将执行每一个服务的线程隔离开来,防止某一项服务出现问题,耗尽整个web容器的资源,进而导致其他的服务也变的不可用,最终导致整个web容器产生雪崩。
线程池隔离:当采用此种隔离策略时,Hystrix会为每一个服务配备一个独立的线程池,当服务请求发送到web容器后,由web容器的线程接收,然后将请求转交给Hystrix为服务提供的线程池,由线程池创建线程完成服务逻辑,这样一来,服务本身不再长时间占据web容器的线程,转交请求之后,web容器的线程可以释放,去为其他的线程服务,即使某一项服务除了问题,也不会影响整个web容器,因为这种方式的调用是异步的。此时接收调用的线程和执行服务逻辑的线程是不同的线程。Hystrix默认采用这种隔离方式。
信号量隔离:当采用此种隔离策略时,Hystrix不会为每一个服务配备线程池,当服务请求发送到web容器后,由web容器的线程接收并且完成服务逻辑的执行。此时接收调用的线程和执行服务逻辑的线程是web容器提供的同一个线程。但是信号量规定了每一个服务所能占据的最大线程数,比如A服务的信号量最大值设置为10,那么它最多就占据web容器的10个线程,不会吃掉web容器的所有线程,避免了其他服务无线程可用,防止了服务雪崩。
线程池隔离 VS 信号量隔离:
隔离方式 | 是否支持超时 | 是否支持降级、熔断 | 隔离原理 | 是否是异步调用 | 资源消耗 |
---|---|---|---|---|---|
线程池隔离 | 支持,可直接返回 | 支持。当请求超时、异常或者线程池Max时,都会导致服务降级 | 每个服务单独用线程池 | 可以是异步,也可以是同步。看调用的方法 | 大,大量线程的上下文切换,容易造成机器负载高。当服务较多时,需要创建的线程池较多,开销较大 |
信号量隔离 | 不支持,如果阻塞,只能通过调用协议(如:socket超时才能返回) | 支持,当请求超时、异常或者信号量达到maxConcurrentRequests后,再请求会触发fallback | 信号量规定了服务的最大线程数 | 同步调用,不支持异步 | 小,只是个计数器 |
其实说白了,两种隔离策略的最大不同就是在于请求调用是否是异步的以及资源开销。我们可以据此来适配不同策略的使用场景:
线程池隔离策略:
适用于请求的网络开销比较大,调用比较耗时的场景,这样可以保证大量的容器(tomcat)线程可用,不会由于服务原因,一直处于阻塞或等待状态,快速失败返回。 比如调用链路长或者调用服务较多(扇出较多)时。
信号量隔离策略:
适用于服务缓存的场景。当我们为某些服务开启了Hystrix的缓存功能后,此类的服务返回就会非常快,不会长时间占用容器的线程资源,采用信号量隔离策略可以减少资源的开销。
资源隔离与服务限流:
服务限流是资源隔离策略下的附属功能,无论是线程池隔离还是信号量隔离,它们最终的目的都可以限制服务的流量。
资源隔离与服务降级、服务熔断的关系:
资源隔离是为了防止同一个web容器内发生的雪崩效应,服务降级、熔断实现是为了防止整条调用链路发生雪崩效应。各有侧重,共同生效,保证了整个系统的高可用。
5、Hystrix服务降级–Hystrix原生注解方式
当服务消费者调用服务时,如果出现超时、异常或者服务提供者宕机的情况,应该有一个兜底方法,向浏览器返回一个可处理的友好提示,防止大量的线程阻塞,并给用户更高的体验,这种兜底方法就叫做服务降级,是正常服务的替代品。
至于服务降级的配置,我们可以放在服务提供者中,也可以