服务容错
服务可靠性问题同时涉及到服务的提供者和消费者。对于服务提供者而言,要做的事情就是在自身发生错误时,快速返回合理的处理结果(快速失败)。而在服务消费者有很多处理的方案,一方面有超时、重试等,另一方面引入服务隔离、服务熔断和服务回退等方案;
1. 相关概念
-
服务消费者容错的需求
- 雪崩效应:微服务中各个服务之间存在着相关的服务依赖,当一个服务变成不可用的时候,导致调用该服务的服务变得不可用,进而扩散到整个下游服务中,此现象称为服务雪崩效应。
- 需求:雪崩效应的解决切入点应该从服务消费者的角度进行解除,首先不能保证所有的服务提供者都不会失败,所以应该确保服务消费者不受已失败的服务提供者的影响或者让影响尽可能低,即服务消费容错的需求;
-
服务隔离
对系统或资源进行分割,从而实现在系统发生故障的时候能够限定传播范围和影响范围,实现在发生故障时之后故障部分的服务是不可用的,其他仍然是可用的。
- 线程隔离:主要是通过线程池进行隔离,在实际使用中可以创建不同的线程池,并按业务进行分类,当某一业务发生故障时,不会使其扩散到其他线程池,进而不会影响其他线程池中的业务。
- 进程隔离:将系统拆分成为过个子系统实现物理隔离,各个子系统运行在独立的容器和JVM之上。
-
服务熔断
设定一个异常的条件,当满足对应的条件的时候,就不再调用服务也是快速失败的一种表现。服务融端中一般会存在一个服务熔断器,会设定以下一些状态:
- 熔断器不启动:对服务的调用不做限制,但对失败进行累计,当达到阈值时会启动响应的熔断机制;
- 熔断器启动:当对服务的调用将直接返回错误,不进行真正的网络调用,同时设定一个时间,当到达是进入半熔断状态;
- 半熔断状态:允许一定量的服务请求,如果调用都成功则认为服务恢复正常,关闭熔断器,否则回到熔断器启动状态;
-
服务回退
当远程服务调用出现问题的时候,不直接抛出对应的异常,而是用相应的机制处理该异常,相当于执行另一条路径上的返回结果给当前的服务调用。他并不能解决实际问题,但可以使用户有较好的体验。
2. 使用Hystrix
他是一款开源的针对分布式系统的延迟和容错库,可用来隔离分布式服务故障,以确保系统的可用性。他其中的核心是 HystrixCommand
HystrixCommand
:需要提供子类,子类中有两个重要的方法:run()
:用于实现所以来的业务逻辑,即实现微服务之间的调用;getFallBack()
:用于实现服务回退等处理逻辑;
- 实现服务隔离:
- 线程池隔离:
- 服务分组+线程池模式:是一种粗粒度实现,以个分配分组配置一个线程池;
- 服务分组+服务+线程池:一个服务分组中的每一个服务配置一个隔离线程池,为不同的命令实现配置不同的线程池名称;
- 混合实现:一个分组配置一个线程池,然后对重要服务进行单独配置。
- 信号量隔离:
- 针对业务请求线程和执行服务依赖的线程是同一个线程时使用此方式。
- 线程池隔离:
- 实现服务熔断:
- 熔断器
HystrixCircuitBreaker
类,该类中存在allowRequest()
方法,该方法用于HystrixCommand
调用来判断当前服务是否已经跟熔断。该判断取决于服务访问的失败率(异常、超时、线程池拒绝、信号量拒绝数量之和 与总访问之和的比) - 当熔断器打开后,在过一定时间后进行重试,此时处于半打开状态,如果重试成果则会关闭开关,否则继续打开。
- 熔断器
- 服务回退:
- 主要是通过
run()
方法超时或有异常时会调用getFallBack()
方法; - 可以通过在
HystrixCommand
中设置fallbackMethod
则会调用相关方法;
- 主要是通过
3. 基本原理
- 服务隔离
Hsytrix
使用命令模式的实现类HystrixCommand
包装实现依赖调用逻辑,将每个类型的业务请求封装成对应的命令,每个命令在单独的线程池中执行,创建好的线程池会放入到ConcurrentHashMap
中,第二次请求可直接获取对应的线程池;HystrixCommand
方式:execute()
:阻塞,但依赖服务正常响应或者抛出异常时返回结果;queue()
:返回Future对象,通过该对象异步得到返回结果;observe()
:返回Observable
对象,立即发出请求,在依赖服务正常响应或者抛出异常时,通过注册的Subscriber
得到返回结果;toObservable
:返回Observable
对象,只有在该对象被订阅时才会发出请求,在依赖服务正常响应或者抛出异常时,通过注册的Subscriber
得到返回结果;- 存在两种
Command
HystrixCommand
和HystrixObservableCommand
前面的适用于前两中方式,后者适用于后两种方式。
- 服务熔断:
- 访问
HystrixCircuitBreaker
接口有三种方法:allowRequest()
方法:每个请求都通过该方法判断是否可被执行,此方法先根据配置对象properties中的配置信息判断熔断器强制打开或关闭属性是否设置,如果打开则直接返回false,如果允许所有请求通过则会调用下面的方法;isOpen()
:返回当前熔断器是否打开。如果打开则返回 true 否则通过度量指标对象metrics
中获取运行时的统计信息进行进一步的判断。markSuccess()
:关闭熔断器,在熔断器处于半开的状态下使用,如果相关的请求执行成功可通过调用该方法关闭熔断器,并重置度量指标对象。
- 访问
- 主要步骤:
- 构件
HystrixCommand
对象,执行业务方法; - 检查服务的熔断器是否开启,开启则执行回退方法;
- 若熔断器关闭,则检查当前服务的线程池是否能够接收新的请求,如果满则回退;
- 线程池接收请求,则调用 run()方法执行相关逻辑;
- 如果服务执行失败,则执行回退方法,并将结果上报监控数据收集器;
- 若服务执行超时,处理方案同上;
- 如果服务执行成功,返回正常结果;
- 若回退方法执行成功,则返回回退结果;
- 若回退方法执行失败,抛出异常;
- 构件