Spring Cloud——服务熔断

1,819 阅读13分钟

简介   

       微服务落地到实际项目中,服务的数量往往非常多,服务之间的相互依赖性也是错综复杂的,一个网络请求通常需要调用多个服务才能完成。如果一个服务不可用,例如网络延迟或故 障,会影响到依赖于这个不可用的服务的其他服务,最后导致整个系统处于瘫痪的状态也就是雪崩效应。

为了解决分布式系统的雪崩效应,分布式系统引进了熔断器机制。熔断器(Circuit Breaker) 一词来源于物理学中的电路知识,它的作用是当电路中出现故障时迅速切断电路,起到保护电 路的作用。

在Spring Cloud 中可以使用Sentinel 和 Hystrix 实现服务熔断与降级。

Hystrix

介绍

       Hystrix 是 Netflix 公司开 源的一个项目,它提供了熔断器功能,能够阻止分布式系统中出现联动故障。Hystrix 是通过隔离服务的访问点阻止联动故障的,并提供了故障的解决方案,从而提高了整个分布式系统的弹性。 

        在github上面 Hystrix的最新版本是1.5.18,并且公告已经停止维护不在发布新版本,目前的稳定版本 1.5.18 已经足够满足现有应用对 Hystrix 的需求。所以在使用Spring Cloud 比较新的版本中不建议使用Hystrix。Hystrix 官方推荐替代的开源组件:Resilience4j 或者使用下面的Sentinel。

Hystrix 的设计原则

Hystrix 的设计原则如下:

  • 防止单个服务的故障耗尽整个服务的 Servlet 容器(例如 Tomcat)的线程资源。 
  • 快速失败机制,如果某个服务出现了故障,则调用该服务的请求快速失败,而不是线程等待。 
  • 提供回退(fallback)方案,在请求发生故障时,提供设定好的回退方案。 
  • 使用熔断机制,防止故障扩散到其他服务。 
  • 提供熔断器的监控组件 Hystrix Dashboard,可以实时监控熔断器的状态。

Hystrix 的工作机制

断路器

       断路器将远程方法调用包装到一个断路器对象中,用于监控方法调用过程的失败。一旦该方法调用发生的失败次数在一段时间内达到一定的阀值,那么这个断路器将会跳闸,在接下来时间里再次调用该方法将会被断路器直接返回异常,而不再发生该方法的真实调用。这样就避免了服务调用者在服务提供者不可用时发送请求,从而减少线程池中资源的消耗,保护了服务调用者。

       虽然断路器在打开的时候避免了被保护方法的无效调用,但是当情况恢复正常时,需要外部干预来重置断路器,使得方法调用可以重新发生。所以合理的断路器应该具备一定的开关转化逻辑,它需要一个机制来控制它的重新闭合。

  • 关闭状态:断路器处于关闭状态,统计调用失败次数,在一段时间内达到一定的阀值后断路器打开。 
  • 打开状态:断路器处于打开状态,对方法调用直接返回失败错误,不发生真正的方法调用。设置了一个重置时间,在重置时间结束后,断路器来到半开状态。 
  • 半开状态:断路器处于半开状态,此时允许进行方法调用,当调用都成功了(或者成功到达一定的比例),关闭断路器,否则认为服务没有恢复,重新打开断路器。 

        断路器的打开能保证服务调用者在调用异常服务时,快速返回结果,避免大量的同步等待,减少服务调用者的资源消耗。并且断路器能在打开一段时间后继续侦测请求执行结果,判断断路器是否能关闭,恢复服务的正常调用。      

服务降级操作

断路器为隔断服务调用者和异常服务提供者防止服务雪崩的现象,提供了一种保护措施。而服务降级是为了在整体资源不够的时候,适当放弃部分服务,将主要的资源投放到核心服务中,保证了系统核心服务的稳定。 在Hystrix中,当服务间调用发生问题时,它将采用备用的Fallback方法代替主方法执行并返回结果,对失败服务进行了服务降级。当调用服务失败次数在一段时间内超过了断路器的阀值时,断路器将打开,不再进行真正的方法调用,而是快速失败,直接执行Fallback逻辑,服务降级,减少服务调用者的资源消耗,保护服务调用者中的线程资源。

资源隔离

        在货船中,为了防止漏水和火灾的扩散,一般会将货仓进行分割,避免了一个货仓出事导致整艘船沉没的悲剧。同样的,在Hystrix中,也采用了舱壁模式,将系统中的服务提供者隔离起来,一个服务提供者延迟升高或者失败,并不会导致整个系统的失败,同时也能够控制调用这些服务的并发度。

       Hystrix通过将调用服务线程与服务访问的执行线程分隔开来,调用线程能够空出来去做其他的工作而不至于因为服务调用的执行阻塞过长时间。 在Hystrix中,将使用独立的线程池对应每一个服务提供者,用于隔离和限制这些服务。于是,某个服务提供者的高延迟或者饱和资源受限只会发生在该服务提供者对应的线程池中。

       DependencyD的调用失败或者高延迟仅会导致自身对应的线程池中的5个线程阻塞,并不会影响其他服务提供者的线程池。系统完全与服务提供者请求隔离开来,即使服务提供者对应的线程完全耗尽,并不会影响系统中的其他请求。

Hystrix实现思路

Hystrix的实现思路: 

  • 它将所有的远程调用逻辑封装到HystrixCommand 或者HystrixObservableCommand对象中,这些远程调用将会在独立的线程中执行(资源隔离),这里使用了设计模式中的命令模式。 
  • Hystrix对访问耗时超过设置阀值的请求采用自动超时的策略。该策略对所有的命令都有效(如果资源隔离的方式为信号量,该特性将失效),超时的阀值可以通过命令配置进行自定义。
  • 为每一个服务提供者维护一个线程池(或者信号量),当线程池被占满时,对于该服务提供者的请求将会被直接拒绝(快速失败)而不是排队等待,减少系统的资源等待。 
  • 针对请求服务提供者划分出成功、失效、超时和线程池被占满等四种可能出现的情况。 
  • 断路器机制将在请求服务提供者失败次数超过一定阀值后手动或者自动切断服务一段时间。 
  • 当请求服务提供者出现服务拒绝、超时和短路(多个服务提供者依次顺序请求,前面的服务提供者请求失败,后面的请求将不会发出)等情况时,执行其Fallback方法,服务降级。 
  • 提供接近实时的监控和配置变更服务。

Hystrix工作流程

    简单的流程如下: 

  1. 构建HystrixCommand 或者HystrixObservableCommand对象。 
  2. 执行命令。 
  3. 检查是否有相同命令执行的缓存。 
  4. 检查断路器是否打开。
  5. 检查线程池或者信号量是否被消耗完。
  6. 调用HystrixObservableCommand#construct 或HystrixCommand#run执行被封装的远程调用逻辑。 
  7. 计算链路的健康情况。 
  8. 在命令执行失败时获取Fallback逻辑。 
  9. 返回成功的Observable。

其他使用

异步与异步回调执行命令

Hystrix除了同步执行命令,还可以异步以及异步回调执行命令。异步执行命令需要定义函数的返回方式为Future

请求合并

        Hystrix还提供了请求合并的功能。多个请求被合并为一个请求进行一次性处理,可以有效减少网络通信和线程池资源。请求合并之后,一个请求原本可能在6毫秒之内能够结束,现在必须等待请求合并周期后(10毫秒)才能发送请求,增加了请求的时间(16毫秒)。请求合并适合在处理高并发和高延迟命令上。

使用 Hystrix Dashboard 监控熔断器的状态

       在微服务架构中,为了保证服务实例的可用性,防止服务实例出现故障导致线程阻塞,而 出现了熔断器模型。熔断器的状况反映了一个程序的可用性和健壮性,它是一个重要指标。 Hystrix Dashboard 是监控 Hystrix 的熔断器状况的一个组件,提供了数据监控和友好的图形化展示界面。

Sentinel 

介绍

       Spring Cloud Alibaba Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。官方文档地址.

Sentinel 基本概念

资源

       资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。

       只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

规则

       围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

Sentinel 功能和设计理念

流量控制设计理念

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
  • 运行指标,例如 QPS、线程池、系统负载等;
  • 控制的效果,例如直接限流、冷启动、排队等。

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

熔断降级设计理念

        Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。在限制的手段上,

        Sentinel 和 Hystrix 采取了完全不一样的方法。Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。

Sentinel 对这个问题采取了两种手段:

  • 通过并发线程数进行限制

        和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

  • 通过响应时间对资源进行降级

       除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

系统自适应保护

         Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

       针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

Sentinel 工作机制

Sentinel 的主要工作机制如下:

  • 对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
  • 根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
  • Sentinel 提供实时的监控系统,方便快速了解目前系统的状态。

使用 Sentinel

Feign 支持

      Sentinel 适配了 Feign 组件。如果想使用,除了引入spring-cloud-starter-alibaba-sentinel 的依赖外还需要 2 个步骤:

  • 配置文件打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true

  • 加入spring-cloud-starter-openfeign  依赖使 Sentinel starter 中的自动化配置类生效:

    org.springframework.cloud spring-cloud-starter-openfeign

这是一个 FeignClient 的简单使用示例:

@FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
    @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
    String echo(@PathVariable("str") String str);
}

class FeignConfiguration {
    @Bean
    public EchoServiceFallback echoServiceFallback() {
        return new EchoServiceFallback();
    }
}

class EchoServiceFallback implements EchoService {
    @Override
    public String echo(@PathVariable("str") String str) {
        return "echo fallback";
    }
}

Feign 对应的接口中的资源名策略定义:httpmethod:protocol://requesturl。@FeignClient 注解中的所有属性,Sentinel 都做了兼容。

EchoService 接口中方法 echo 对应的资源名为 GET:http://service-provider/echo/{str}

参考

深入理解Spring Cloud与微服务构建
Sentinel 官方文档地址
Spring Cloud 微服务架构进阶