基于Hystrix的spring boot的容错机制
摘要:在系统服务中,会存在因为过多的用户请求从而引发出系统崩溃的现象,为了防范这种现象的产生在系统服务中引入了容错机制。在微服务架构系统服务中,为了防止产生系统崩溃现象,在spring boot中加入了Hystrix的容错组件。Hystrix是一个实现超时机制和断路器模式的工具类库。当架构中的某一组件发生故障时,Hystrix组件通过断路器等一系列的服务保护机制,保证服务调用者在调用异常服务时快速的返回结果,避免大量的同步等待。[1]微服务架构引入Hystrix组件,相比于传统单一进程的架构下,在某一组件发生故障时,Hystrix组件可以将故障隔离在单个服务中,使其他服务可以通过重试,平稳退化等机制实现服务容错。本篇文章将将介绍容错机制以及Hystrix的容错实现。
关键字:微服务架构,spring boot,容错机制,Hystrix
前言:微服务架构是一种架构风格和架构思想。根据系统业务按照功能拆分为更加细粒度的服务,所拆分的每一个服务就是一个应用,这些应用对外提供公共的API,可以独立承担对外服务的职责,通过这种思想方法所开发的软件服务实体就是“微服务”,而围绕微服务思想构建的一系列体系结构就是“微服务架构”。 在微服务架构中存在很多组件:Eureka实现了微服务的注册和发现,Ribbon实现了客户端的负载均衡,Feign实现了声明式的API的调用。相对于传统的单体架构而言,微服务架构有着显著的优势,但是在提供方便的同时,也带来了一些挑战。
随着大数据时代的到来,网络上充斥着各种各样的系统。这些系统每天都要受到大规模的用户访问,过多的并发用户请求,会导致用户的请求延迟很久没有响应,在等待很长时间后最终失败。请求失败会导致用户重新刷新页面并且再次请求,这样会增加服务器的负载,最终导致整个系统崩溃。为了使系统正常的运行,出现了多种的容错机制。在微服务架构中,Hystrix组件便是整个系统的容错组件。
spring cloud Hystrix是基于Netflix的开源框架Hystrix实现的,该框架的使用目标在于通过控制那些访问远程系统,服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix的容错方式有隔离,服务回退,断路器。Hystrix的隔离策略有两种:分别是线程隔离和信号量隔离。服务回退是在业务发生异常时,触发fallback,服务业务回退到开发人员设定的界面或者服务。断路器是在一段时间内检测到大量的相似的错误时,就会在之后的一段时间内,强迫对该服务的调用快速失败,就是不再请求所依赖的服务,这样就减少了服务故障的等待时间。Hystrix组件的使用使得spring boot架构存在较高的容错性,提升了系统的可用性和容错性。
\1. 雪崩效应
分布式系统中经常会出现某个基础服务不可用导致整个系统不可用的情况,这种现象被称为服务的雪崩效应。服务雪崩效应是一种因“服务提供者的不可用”导致“服务调用者不可用”,并将不可用逐渐放大的现象。[2]如图1-1所示:
图1- 1
上图中, A为服务提供者, B为A的服务调用者, C和D是B的服务调用者. 当A的不可用,引起B的不可用,并将不可用逐渐放大C和D时, 服务雪崩就形成了。
服务雪崩的过程可以分为三个阶段:
服务提供者不可用;
重试加大请求流量;
服务调用者不可用;
服务雪崩的每个阶段都可能由不同的原因造成,总结如图1-2所示
图1- 2
应对策略如图1-3所示
图1- 3
\2. 容错机制
为了应对产生的雪崩效应,微服务架构中采用了三种容错机制:舱壁隔离模式,断路器器模式,服务回退。
2.1舱壁隔离模式
舱壁隔离模式就是仿照造船行业,利用舱壁将不同的船舱隔离起来,这样如果一个船舱进了水,只损失一个船舱,其他船舱可以不受影响。
在 Hystrix 中,主要通过线程池来实现资源隔离。通常在使用的时候我们会根据调用的远程服务划分出多个线程池。例如调用产品服务的 Command 放入 A 线程池,调用账户服务的 Command 放入 B 线程池。这样做的主要优点是运行环境被隔离开了。这样就算调用服务的代码存在 bug 或者由于其他原因导致自己所在线程池被耗尽时,不会对系统的其他服务造成影响。
2.2断路器模式
断路器属于弹性容错,在一定的条件下会自动打开关闭。断路器的开关由关闭到开关的状态是通过当前服务健康状况(服务的健康状况=请求失败数/请求总数)和设定阈值(默认为10秒内的20次故障)比较决定的。当断路器开关关闭时,请求被允许通过断路器,如果当前健康状况高于设定阈值,开关继续保持关闭;如果当前的健康状况低于设定阈值,开关切换为打开状态。当断路器开关打开时,请求被禁止通过;[3]
断路器的开关可以保证服务的调用者在调用异常服务时,快速返回结果,避免大量的同步等待,并且断路器能在一段时间后继续侦测请求执行结果,提供恢复服务调用的可能。
2.3 服务回退
降级,通常指务高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。Hystrix提供的降级主要是为了容错,保证当前服务不受依赖服务故障的影响,从而提高服务的健壮性。要支持回退或降级处理,可以重写HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法。
\3. Hystrix 的容错实现
在微服务架构中,Hystrix是通过以下几点实现延迟和容错。
1 包裹请求:使用HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
2 跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以自动或手动跳闸,停止请求该服务一段时间。
3 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
4 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
5 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员自行提供,例如返回一个缺省值。
6 自我修复:断路器打开一段时间后,会自动进入“半开”状态。[4]
3.1 设置超时
在系统服务中,一次远程调用对应着一个线程,如果服务响应过慢,这个线程就得不到释放,并且会持续性的消耗资源。如果存在较多的线程资源得不到释放,就会导致服务资源短缺,从而导致服务不可用。
为了防止因为线程占用的情况,必须为网络设置超时。当服务请求超过设定的时间后,服务停止响应,并提示服务响应超时。这样就会防止大量的线程被占用从而导致系统崩溃的现象。
3.2 隔离
Hystrix的隔离存在两种:线程隔离和信号量隔离。
THREAD(线程隔离):使用该方法,HystrixCommand将在单独的线程上执行,并发请求受到线程池中的线程数量限制。
SEMAPHORE(信号量隔离):使用该方法,HystrixCommand将在调用线程上执行,并开销相对较少,并发请求受到信号量的个数限制。[5]
3.2.1线程隔离
线程隔离是通过将发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,使得请求线程可以快速失败,从而避免依赖问题扩散。
线程隔离的优点:
保护应用程序以免受来自依赖故障的影响,指定依赖线程池饱和不会影响应用程序的其余部分。
当引入新客户端lib时,即使发生问题,也是在本lib中,并不会影响到其他内容。
当依赖从故障恢复正常时,应用程序会立即恢复正常的性能。
当应用程序一些配置参数错误时,线程池的运行状况会很快检测到这一点(通过增加错误,延迟,超时,拒绝等),同时可以通过动态属性进行实时纠正错误的参数配置。
如果服务的性能有变化,需要实时调整,比如增加或者减少超时时间,更改重试次数,可以通过线程池指标动态属性修改,而且不会影响到其他调用请求。
线程池的主要缺点是增加了计算开销。每个命令的执行都在单独的线程完成,增加了排队、调度和上下文切换的开销。因此,要使用Hystrix,就必须接受它带来的开销,以换取它所提供的好处。
3.2.2 信号量隔离
信号量隔离主要是通过控制并发请求量,防止请求线程大面积阻塞,从而达到限流和防止雪崩的目的。当依赖延迟极低的服务时,线程池隔离技术引入的开销超过了它所带来的好处。这时候可以使用信号量隔离技术来代替,通过设置信号量来限制对任何给定依赖的并发调用量。
线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务,Hystrix会通过响应中断的方式通知线程立即结束并返回。
\4. Hystrix 容错实现过程
4.1在spring cloud项目中使用Hystrix组件需要先在项目中加入eureka和Hystrix的依赖
代码如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
4.2 编辑配置文件。在application.yml文件中添加Eureka服务实例的端口号,服务端地址等,代码如下:
server:
port: 8030 #指定该Eureka实例的端口号
eureka:
instance:
prefer-ip-address: true #是否显示IP地址
client:
service-url:
defaultZone: http://localhost:8761/eureka #指定Eureka服务端地址
spring:
application:
name: microservice-eureka-user-hystrix #指定应用名称
4.3 在工程主类Application中使用@EnableCircuitBreaker注解开启断路器功能
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class Application {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main (String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.5修改用户控制类,在findOrdersByUser()方法上添加@HystrixCommand注解来制定回调方法。
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/findOrdersByUser/{id}")
@HystrixCommand(fallbackMethod="fallbackInfo")
public String findOrdersByUser(@PathVariable String id){
int oid=123;
return this.restTemplate.getForObject("http://microservice-eureka-order/order/"+oid,String.class);
}
public String fallbackInfo(@PathVariable String id){
return "服务不可用,请稍后再试!";
}
}
4.6验证服务回退机制
开启注册中心,服务提供者(7900,7901)和服务消费者后,注册中心显示为:
访问Http://localhost:8030/findOrdersByUser/1,浏览器显示为:
停止7901服务,此时页面的显示信息为:
\5. 总结
本文介绍了容错机制和容错机制的措施实现,还介绍了Hystrix的服务回退的实现,以及Hystrix的容错方式。通过Hystrix的容错机制的使用,提高了系统整体的可用性。
参考文献
[1] 周立.Spring Cloud与Docker 微服务架构实战[M]. 三河市:三河市良远印务有限公司, 2018. 94-97.
[2] 周立.Spring Cloud与Docker 微服务架构实战[M]. 三河市:三河市良远印务有限公司, 2018. 94-97.
[3] 黑马程序员.微服务架构基础[M]. 北京市:人民邮电出版社, 2018. 55-56.
[4] 周立.Spring Cloud与Docker 微服务架构实战[M]. 三河市:三河市良远印务有限公司, 2018. 94-97.
[5] 周立.Spring Cloud与Docker 微服务架构实战[M]. 三河市:三河市良远印务有限公司, 2018. 94-97.