架构设计之降级和熔断

1,862 阅读13分钟

1. 初识降级和熔断

1.1 什么是降级?

降级意味着多种方案,当系统出现问题的时候,你有一个备选方案可以马上切换,比如有一个接口的功能是实时预测未来一个月某个商品的采购数量,突然间依赖的上游系统出现问题了,那么我们的接口就完全不可用了吗?显然这是不应该的,这时我接口就可以降级,返回昨天实时计算出来的结果,虽然准确性可能差一点,但系统能够正常运转,降级也分为 自动降级手动降级

  • 自动降级:是系统自动检测到问题时自动切换,
  • 手动降级:是系统检测到问题报警,人为的通过开关进行切换

降级代表着系统相比降级之前其功能表现不如之前的完美(这个具体体现在功能准确性,可用性上等,如上面接口的例子)

1.2 什么熔断?

通俗来讲,熔断指的是遇到危险了,必须马上停掉,比如生活中的电流过大,必须马上切断,否则就发生了火灾了,熔断之后就会导致断电,完全不可用,在一个系统中:

  • 假设一个接口部署了10台机器(分布式),突然某一台机器的接口调用情况正确率降到90%,那么这台机器肯定出现问题了,这个时候就需要熔断这台机器,把这台机器从整个集群中摘掉,从而保证用户的请求100%的正确
  • 假设一个系统中有很多功能,这些功能有些是核心功能,有些是非核心功能,那么在一些大促中,我们可能熔断掉一些非核心功能,从而保证核心功能的流转(登录和注册,登录属于核心,注册是属于非核心)

1.3 什么是服务降级

什么是服务降级?当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。

如果还是不理解,那么可以举个例子:假如目前有很多人想要给我付钱,但我的服务器除了正在运行支付的服务之外,还有一些其它的服务在运行,比如搜索、定时任务和详情等等。然而这些不重要的服务就占用了JVM的不少内存与CPU资源,为了能把钱都收下来(钱才是目标),我设计了一个动态开关,把这些不重要的服务直接在最外层拒掉,这样处理后的后端处理收钱的服务就有更多的资源来收钱了(收钱速度更快了),这就是一个简单的服务降级的使用场景。

2. 为什么需要熔断和降级?

不管是降级还是熔断,都是为了保证了系统的稳定性,可用性

  • 降级往往代表系统功能部分不可用
  • 熔断代表的是完全不可用

2.1 使用场景

服务降级主要用于什么场景呢?

当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用

3. 熔断和降级策略

在业务系统时,降级 在编码时需要考虑好备选方案,和业务确认方案的合理性,熔断 在编码时需要分离核心功能和非核心功能,梳理上下游依赖关系,防止强依赖引起的系统的雪崩,这些是业务系统功能设计时需要经常考虑的。

所有业务系统都需要考虑的东西,就意味着可以优化,可以剥离,抽象出来,做成公共组件,中间件,形成通用性。

上面有提到,降级和熔断的最终目的都是保证系统的稳定性,可靠性,保证核心服务可用,那么在形成中间件时具体措施是什么呢?

3.1 降级策略

  1. 超时降级(调用服务时超时返回默认值或者其它处理办法)
  2. 失败次数降级(服务可用率下降时降级)
  3. 限流也是降级的一种办法
  4. 故障降级(依赖的外系统发生故障时降级)
  5. 拒绝服务降级

3.2 熔断策略

  1. 系统攻击熔断(当某个服务遭遇流量攻击时,可以熔断这个服务)
  2. 涉及核心功能运行时的熔断(下单和评论功能,关键时刻可以熔断评论功能)

不管降级还是熔断,在设计时都要考虑:降级熔断算法,恢复机制,报警。这些是必备的,不能系统降级了或者熔断了就无法回复之前的情况,也不能不报警,要不然开发人员都不知道,这还得了。

3.3 熔断和降级比较

  1. 两者的目的相当
  2. 两者的最终的表现的相同
  3. 粒度一样,大多数都是服务级别的粒度,也有可能是方法级别的
  4. 自治性要求比较高(尽可能的智能化)
  5. 降级一般是客户端处理,熔断是在服务端处理的

4. 核心设计

4.1 服务降级设计

1. 分布式开关

根据上述需求,我们可以设置一个分布式开关,用于实现服务的降级,然后集中式管理开关配置信息即可。具体方案如下: image.png

自动降级时机

  • 超时降级 —— 主要配置好超时时间和超时重试次数和机制,并使用异步机制探测恢复情况
  • 失败次数降级 —— 主要是一些不稳定的API,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
  • 故障降级 —— 如要调用的远程服务挂掉了(网络故障、DNS故障、HTTP服务返回错误的状态码和RPC服务抛出异常),则可以直接降级
  • 限流降级 —— 当触发了限流超额时,可以使用暂时屏蔽的方式来进行短暂的屏蔽

当我们去秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时开发者会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。

2. 配置中心

微服务降级的配置信息是集中式的管理,然后通过可视化界面进行友好型的操作。配置中心和应用之间需要网络通信,因此可能会因网络闪断或网络重启等因素,导致配置推送信息丢失、重启或网络恢复后不能再接受、变更不及时等等情况,因此服务降级的配置中心需要实现以下几点特性,从而尽可能的保证配置变更即使达到: image.png

  • 启动主动拉取配置 —— 用于初始化配置(减少第一次定时拉取周期)
  • 发布订阅配置 —— 用于实现配置及时变更(可以解决90%左右的配置变更)
  • 定时拉取配置 —— 用于解决发布订阅失效或消失丢失的情况(可以解决9%左右的发布订阅失效的消息变更)
  • 离线文件缓存配置 —— 用于临时解决重启后连接不上配置中心的问题
  • 可编辑式配置文档 —— 用于直接编辑文档的方式来实现配置的定义
  • 提供Telnet命令变更配置 —— 用于解决配置中心失效而不能变更配置的常见

3,处理策略

当触发服务降级后,新的交易再次到达时,我们该如何来处理这些请求呢?从微服务架构全局的视角来看,我们通常有以下是几种常用的降级处理方案:

  • 页面降级 —— 可视化界面禁用点击按钮、调整静态页面
  • 延迟服务 —— 如定时任务延迟处理、消息入MQ后延迟处理
  • 写降级 —— 直接禁止相关写操作的服务请求
  • 读降级 —— 直接禁止相关度的服务请求
  • 缓存降级 —— 使用缓存方式来降级部分读频繁的服务接口

针对后端代码层面的降级处理策略,则我们通常使用以下几种处理措施进行降级处理:

  • 抛异常
  • 返回NULL
  • 调用Mock数据
  • 调用Fallback处理逻辑

4.2 高级特性

我们已经为每个服务都做好了一个降级开关,也已经在线上验证通过了,感觉完全没问题了。

场景一:某一天,运营搞了一次活动,突然跑过来说,现在流量已经快涨到上限了,有没有批量降级所有不重要服务的方式?开发一脸懵逼的看着,这又不是操作DB,哪里有批量操作呀。

场景二:某一天,运营又搞事了,说我们等下要搞一个活动,让我们赶紧提前把不重要的服务都降级了,开发又是一脸懵逼,我怎么知道要降级哪些服务呀。

反思:服务降级的功能虽然是实现了,可是没有考虑实施时的体验。服务太多,不知道该降级哪些服务,单个操作降级速度太慢……

分级降级

当微服务架构发生不同程度的情况时,我们可以根据服务的对比而进行选择式舍弃(即丢车保帅的原则),从而进一步保障核心的服务的正常运作。

如果等线上服务即将发生故障时,才去逐个选择哪些服务该降级、哪些服务不能降级,然而线上有成百上千个服务,则肯定是来不及降级就会被拖垮。同时,在大促或秒杀等活动前才去梳理,也是会有不少的工作量,因此建议在开发期就需要架构师或核心开发人员来提前梳理好,是否能降级的初始评估值,即是否能降级的默认值。

为了便于批量操作微服务架构中服务的降级,我们可以从全局的角度来建立服务重要程度的评估模型,如果有条件的话,建议可以使用 层次分析法(The analytic hierarchy process,简称AHP)  的数学建模模型(或其它模型)来进行定性和定量的评估(肯定比架构师直接拍脑袋决定是否降级好很多倍,当然难度和复杂度也会高许多,即你需要一个会数学建模人才),而层次分析法的基本思路是人对一个复杂的决策问题的思维和判断过程大体上是一样的。

以下是个人给出的最终评价模型,可作为服务降级的评价参考模型进行设计:

我们利用数学建模的方式或架构师直接拍脑袋的方式,结合服务能否降级的优先原则,并根据台风预警(都属于风暴预警)的等级进行参考设计,可将微服务架构的所有服务进行故障风暴等级划分为以下四种:

评估模型

  • 蓝色风暴 —— 表示需要小规模降级非核心服务
  • 黄色风暴 —— 表示需要中等规模降级非核心服务
  • 橙色风暴 —— 表示需要大规模降级非核心服务
  • 红色风暴 —— 表示必须降级所有非核心服务

设计说明

  • 故障严重程度为:蓝色<黄色<橙色<红色
  • 建议根据二八原则可以将服务划分为:80%的非核心服务+20%的核心服务

以上模型只是整体微服务架构的服务降级评估模型,具体大促或秒杀活动时,建议以具体主题为中心进行建立(不同主题的活动,因其依赖的服务不同,而使用不同的进行降级更为合理)。当然模型可以使用同一个,但其数据需要有所差异。最好能建立一套模型库,然后实施时只需要输入相关服务即可输出最终降级方案,即输出本次大促或秒杀时,当发生蓝色风暴时需要降级的服务清单、当发生黄色风暴时需要降级的服务清单……

降级权值

微服务架构中有服务权值的概念,主要用于负载时的权重选择,同样服务降级权值也是类似,主要用于服务降级选择时的细粒度优先级抉择。所有的服务直接使用以上简单的四级划分方式进行统一处理,显然粒度太粗,或者说出于同一级的多个服务需要降级时的 降级顺序 该如何?甚至我想要人工智能化的 自动降级,又该如何更细粒度的控制?

基于上述的这些AI化的需求,我们可以为每一个服务分配一个降级权值,从而便于更加智能化的实现服务治理。而其评估的数值,同样也可以使用数学模型的方式进行 定性 与 定量 的评估出来,也可以架构师根据经验直接拍脑袋来确定。

5. 技术选型

熔断降级技术主要有 Netflex的 Hystrix、阿里的 Sentinel

5.1 Hystrix 原理

Hystrix有Java和Go版本的,Java版本的是Netflix公司开发并开源的,Go版本的是由afex(个人)创建的,代码库地址如下:

Hystrix引入以下手段来保护系统:

  1. 资源隔离(线程池和信号量两种手段的隔离)
  2. 限流
  3. 降级
  4. 熔断(断路器)

Hystrix如何设计实现这些手段呢?

  1. 使用命令模式将所有对外部服务(或依赖关系)的调用包装在 HystrixCommandHystrixObservableCommand 对象中,并将该对象放在单独的线程中执行
  2. 每一个依赖都有自己对应的线程池或者信号量,线程池耗尽时,拒绝请求
  3. 维护请求的各种状态(成功,失败,超时的次数)
  4. 当错误率到达一定阈值时,进行熔断,过一定的时间后又恢复
  5. 提供降级,失败,成功,熔断后的回调逻辑
  6. 实时的监控指标和配置信息的修改

参考资料

1.微服务架构—服务降级 2. 微服务-高并发情况下接口降级和熔断策略