快捷搜索:

(无代码)服务降级和Hystrix详解

服务降级

一,服务分级

对每个微服务进行等级管理后,降级一般是从最外围、等级最低的服务开始。

服务分级可参考

一个简单例子如下图:

二,服务熔断

降级有个类似的词称为服务熔断,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。

服务降级就是当某个服务熔断后,服务端准备一个本地的回退回调,返回一个缺省值。

一个基本的服务熔断器结构在实现上一般有三个状态机:

(1)Closed:熔断器关闭状态,调用失败次数积累,到了阈值(或一定比例)则启动熔断机制;

(2)Open:熔断器打开状态,此时对下游的调用都内部直接返回错误,不走网络,但设计了一个时钟选项,默认的时钟达到了一定时间(这个时间一般设置成平均故障处理时间,也就是MTTR),到了这个时间,进入半熔断状态;

(3)Half-Open:半熔断状态,允许定量的服务请求,如果调用都成功(或一定比例)则认为恢复了,关闭熔断器,否则认为还没好,又回到熔断器打开状态;

三,服务降级与服务熔断的区别

    触发原因不大一样。服务熔断一般是某个下游服务发生故障引起,而服务降级一般是从整体负荷考虑。 管理目标层次不一样。熔断是一个框架级的处理,每个服务都需要,无层级之分,而降级一般需要对业务有层级之分。

Spring Cloud Hystrix

一 ,概述

在软件架构领域,容错特指容忍并防范局部错误,不让这种局部错误不断扩大。我们在识别风险领域,风险可以分为已知风险和未知风险,容错直接应对的就是已知风险,这就要求针对的场景是:系统之间调用延时超时、线程的数量急剧飙升导致CPU使用率升高、集群服务器磁盘被打满等等。面对容错,我们一般都会要求提供降级方案,而且强调不可进行暴力降级(比如把整个论坛功能降掉直接给用户展现一个大白板),而是将一部分数据缓存起来,也就是要有拖底数据。

在一个分布式系统中,必然会有部分系统的调用会失败。Hystrix是一个通过添加超时容错和失败容错逻辑来帮助你控制这些分布式系统的交互。Hystrix通过隔离服务之间的访问,阻止他们之间的级联故障以及提供后背选项来实现进行丢底方案,从而提高系统的整体弹性。

Hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制,Hystrix提供了熔断、隔离、Fallback、cache、监控等功能,能够在一个、或多个依赖同时出现问题时保证系统依然可用。

Hytrix官网描述:

Introduction:
Hystrix is a latency and fault tolerance library designed to isolate points of access to remote systems, 
services and 3rd party libraries, 
stop cascading failure and enable resilience in complex distributed systems where failure is inevitable.

大意是:Hytrix是一个延迟和容错库,旨在隔离远程系统、服务和第三方库,阻止级联故障,在复杂的分布式系统中实现恢复能力。

二 ,设计目标

查看Hytrix官网https://github.com/Netflix/Hystrix/wiki中对设计目标有如下描述:

Hystrix is designed to do the following:
1.Give protection from and control over latency and failure from dependencies accessed 
(typically over the network) via third-party client libraries.
2.Stop cascading failures in a complex distributed system.
3.Fail fast and rapidly recover.
4.Fallback and gracefully degrade when possible.
5.Enable near real-time monitoring, alerting, and operational control.

翻译如下:

1.通过客户端库对延迟和故障进行保护和控制。 2.在一个复杂分布式系统中防止级联故障。 3.快速失败和迅速恢复。 4.在合理情况下回退和优雅降级。 5.开启近实时监控、告警和操作控制。

三,解决的主要内容

(一)隔离

隔离的基本要求是:限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。Spring Cloud Hytrix下针对隔离主要指的是线程池隔离和信号量隔离。如下图所示,可以很明显的说明信号量隔离和线程池隔离的主要区别:线程池方式下,业务请求线程和执行依赖的服务线程不是同一个线程;信号量方式下业务请求线程和执行依赖的线程是同一个线程。

1.线程和线程池

线程隔离指每个服务都为一个个独立的线程组,当某个服务出现问题时,不会导致整个服务瘫痪。由于线程隔离会带来线程开销,有些场景(比如无网络请求场景)可能会因为用开销换隔离得不偿失,为此hystrix提供了信号量隔离,当服务的并发数大于信号量阈值时将进入fallback。

如下图,客户端(第三方,网络调用等)依赖和请求线程运行在不同的线程上,可以将他们从调用线程隔离开来,这样调用者就可以从一个耗时太长的依赖中隔离,也可以为不同的请求开启不同的线程池,彼此之间不相互干扰。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Harjy1wG-1596991233059)(C:UsersCR553Pictures292.png)]

传统上,我们在实现线程池隔离的手段上,一般有以下两种方式:

    方式一:使用一个大的线程池,固定线程池的大小,比如1000,通过权重的思路为某个方法分配一个固定大小的线程数,比如为某个方法请求分配了10个线程,按照实现方式有“保守型”和“限制性”,具体见以后博客中和github中的代码举例。 方式二:利用ConcurrentHashMap来存储线程池,key是方法名,值是每个方法对应的一个ThreadPool。当请求到来的时候,我们获取方法名,然后直接从Map对象中取到响应的线程池去处理。

对于方法一而言,严格意义上讲,它并不属于线程池隔离,因为它只有一个公共的线程池,然后来让大家瓜分,不过也达到了隔离的效果。在Spring Cloud Hytrix的线程池隔离上,我们使用的是方式二。对于以上两种方式,线程池的粒度都是作用在方法上,我们可以结合实际情况也可以按组来分。

线程隔离的好处 整个应用在客户端调用失效的情况下也能健康的运行,线程池能够保证这个线程的失效不会影响应用的运行。当失效的客户端调用回复的时候,这个线程池也会被清理并且应用会立马回复健康,比tomcat那种长时间的恢复要好很多。简而言之,线程隔离能够允许在不引起中断的情况下优雅的处理第三方调用的各种问题。

线程隔离的缺点 主要缺点是增加了上下文切换的开销,每个执行都涉及到队列,调度和上下文切换。不过NetFix在设计这个系统的时候,已经决定接受这笔开销,以换取他的好处。对于不依赖网络访问的服务,比如只依赖内存缓存,就不适合用线程池隔离技术,而是采用信号量隔离。

2.信号量隔离

可以使用信号量(或者计数器)来限制当前依赖调用的并发数,而不是使用线程池或者队列。如果客户端是可信的,且能快速返回,可以使用信号量来代替线程隔离,降低开销。信号量的大小可以动态调节,线程池却不行。

HystrixCommand和HystrixObserverCommand提供信号量隔离在下面两个地方:

    Fallback:当Hystrix检索fallback的时候,他心总是调用tomcat线程上执行此操作 如果你设置execution.isolation.strategy为SEMAPHORE的时候,Hystrix会使用信号量代替线程池去限制当前 调用Command的并发数。 对于不依赖网络访问的服务,就不适合使用线程池隔离,而是采用信号量隔离。

(二)降级机制

超时降级、资源不足时(线程或信号量)降级,降级总之是一种退而求其次的方式,所谓降级,就是指在Hystrix执行费核心链路功能失败的情况下,我们应该如何返回的问题,根据业务场景的不同,一般采用以下两种模式进行降级:

    第一种:(最常用)如果服务失败,则我们通过fallback进行降级,返回静态值。 第二种:调用备选方案

具体降级回退方案如:Fail Fast(快速失败)、Fail Silent(无声失败)、Fallback:Static(返回默认值)、Fallback:Stubbed(自己组装一个值返回)、Fallback:Cache via Network(利用远程缓存)、Primary + Secondary with fallback(主次方式回退)

这里需要注意回退处理方式不适合的场景,如以下三种场景我们不可以使用回退处理:

    写操作 批处理 计算

(三)熔断器

当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复,类似于电闸。下图是基本源码中的一个处理流程图:

断路器打开还是关闭的步骤如下

  1. 假定请求的量超过预定的阈值(circuitBreakerRequestVolumeThreshold)
  2. 再假定错误百分比超过了设定的百分比(circuitBreakerErrorThresholdPercentage)
  3. 断路器会从close状态到open状态
  4. 当打开的状态,会短路所有针对该断路器的请求
  5. 过了一定时间(circuitBreakerSleepWindowInMilliseconds(短路超过一定时间会重新去请求)),下一
  6. 个请求将通过,不会被短路(当前是half-open状态)。如果这个请求失败了,则断路器在睡眠窗口期间返回open状态,如果请求成功,则断路器返回close状态,并重新回到第一步逻辑判断。

(四)缓存

提供了请求缓存、请求合并实现。

1.Hystrix缓存策略的命令执行流程

2.请求合并实现

利用请求合并可以减少线程和网络连接,开发人员不必单独提供一个批量请求接口就可以完成批量请求。 在Hystrix中进行请求合并也是要付出一定代价的,请求合并会导致依赖服务的请求延迟增高,延迟的最大值是合并时间窗口的大小,默认为10ms,当然我们也可以通过hystrix.collapser.default.timerDelayInMilliseconds属性进行修改,如果请求一次依赖服务的平均响应时间是20ms,那么最坏情况下(合并窗口开始是请求加入等待队列)这次请求响应时间就会变成30ms。在Hystrix中对请求进行合并是否值得主要取决于Command本身,高并发度的接口通过请求合并可以极大提高系统吞吐量,从而基本可以忽略合并时间窗口的开销,反之,并发量较低,对延迟敏感的接口不建议使用请求合并。

请求合并的流程图如下:

可以看出Hystrix会把多个Command放入Request队列中,一旦满足合并时间窗口周期大小,Hystrix会进行一次批量提交,进行一次依赖服务的调用,通过充写HystrixCollapser父类的mapResponseToRequests方法,将批量返回的请求分发到具体的每次请求中。

(五)实时监控

通过仪表盘等进行统计后,从而进行实时监控、报警、控制,最终依据这些来修改配置,得到最佳的选择。

四,工作流程介绍

基本流程图如下:

  1. 创建HystrixCommand或者HystrixObservableCommand对象。用来表示对以来服务的请求操作。从命名就能看的出来,使用的是命令模式,其中HystrixCommand返回的是单个操作结果,HystrixObservableCommand返回多个结果
  2. 命令执行,共有4中方法执行命令: execute():用户执行,从依赖的服务里返回单个结果或抛出异常 queue():异步执行,直接返回一个Future对象 observe():放回observable对象,代表了多个结果,是一个Hot Observable toObservable():返回Observable对象,但是是一个 Cold Observable(Hystrix大量的使用了RxJava,想更容易的理解Hystrix的,请自行百度RxJava进行阅读。)
  3. 结果是否被缓存。如果已经启用了缓存功能,且被命中,那么缓存就会直接以Observable对象返回
  4. 断路器是否已打开,没有命中缓存,在执行命令前会检查断路器是否已打开:

断路器已打开,直接执行fallback 断路器关闭,继续往下执行

  1. 线程池And信号量Or请求队列是否已被占满 如果与该命令有关的线程池和请求队列,或者信号量已经被占满,就直接执行fallback
  2. 执行HystrixObservableCommand.construct () 或 HystrixCommand.run() 方法。如果设置了当前执行时间超过了设置的timeout,则当前处理线程会抛出一个TimeoutyException,如果命令不在自身线程里执行,就会通过单独的计时线程来抛出异常,Hystrix会直接执行fallback逻辑,并忽略run或construct的返回值。
  3. 计算断路器的健康值。
  4. fallback处理。
  5. 返回成功的响应。

五,仪表盘讲解

Spring Cloud Hystrix Dashboard是一个可以监控HystrixCommand的可视化图形界面,由于某种原因,如网络延迟、服务故障等,这时候可以借助dashboard提供的可视化界面监控各个Hystrix执行的成功率、调用成功数、失败数量、最近十分钟的流量图等等,根据这些数据我们就可以进行错误排查以及进行服务的优化等。Hystrix Dashboard只能对单个服务进行监控,实际项目中,服务通常集群部署,这时候可以借助Turbine进行多个服务的监控。

(一)监控单体应用 不管是监控单体应用还是Turbine集群监控,我们都需要一个Hystrix Dashboard,当然我们可以在要监控的单体应用上继续添加功能,让它也具备仪表盘的功能,但是这样并不符合我们微服务的思想,所以,Hystrix仪表盘我还是单独创建一个新的工程专门用来做Hystrix Dashboard。Hystrix Dashboard仪表盘是根据系统一段时间内发生的请求情况来展示的可视化面板,这些信息时每个HystrixCommand执行过程中的信息,这些信息是一个指标集合和具体的系统运行情况。如Hystrix Dashboard界面图:

输入相关数据,得到如下仪表盘:

(二)Turbine集群监控 在实际应用中,我们要监控的应用往往是一个集群,这个时候我们就得采取Turbine集群监控了。Turbine有一个重要的功能就是汇聚监控信息,并将汇聚到的监控信息提供给Hystrix Dashboard来集中展示和监控。

在实际项目中,这种实时监控有点耗性能,通常采用消息中间件如RabbitMQ等,我们接口调用把Hystrix的一些信息收集到RabbitMQ中,然后Turbine从RabbitMQ中获取监控的数据。

总结

Hystrix是一个通过添加超时容错和失败容错逻辑来帮助你控制这些分布式系统的交互。

Hystrix设计目标为在复杂的分布式系统中防止级联故障,在服务调用中异常时快速失败和迅速恢复,并在合理情况下回退和优雅降级;对微服务实时监控、告警和操作控制。

Hystrix设计要考虑的主要内容分为隔离,降级机制,熔断器,缓存,实时监控。

隔离分为信号量隔离和线程池隔离两类,两者的区别在于业务请求线程和执行依赖的服务线程是否同一个线程。

降级机制分为使用fallback降级或者调用备用方案两类,具体回退方案需要视具体情况和需求改变。

熔断器状态分为三个阶段,打开,关闭,半开。服务正常时熔断器关闭,服务异常时熔断器打开,异常指的是一定时间内调用次数超过设定阈值且错误比例超过设定阈值。熔断器关闭一段时间后进入到半开状态,此时尝试提供服务,若成功数超过设定阈值,则熔断器关闭,若失败则熔断器再次打开。

离和线程池隔离两类,两者的区别在于业务请求线程和执行依赖的服务线程是否同一个线程。

降级机制分为使用fallback降级或者调用备用方案两类,具体回退方案需要视具体情况和需求改变。

熔断器状态分为三个阶段,打开,关闭,半开。服务正常时熔断器关闭,服务异常时熔断器打开,异常指的是一定时间内调用次数超过设定阈值且错误比例超过设定阈值。熔断器关闭一段时间后进入到半开状态,此时尝试提供服务,若成功数超过设定阈值,则熔断器关闭,若失败则熔断器再次打开。

hystrix提高效率可考虑缓存技术和请求合并方式。

经验分享 程序员 职场和发展