基础
在一些场景下,你既可以用熔断,也可以用降级。比如说在响应时间超过阈值之后,你可以考虑选择熔断,完全不提供服务;你也可以考虑降级,提供有损服务。
原则上来说,是应该优先考虑使用降级的。然而有些服务是无法降级的,尤其是写服务。例如你从前端接收数据,然后写到数据库,这种场景是无法降级的。另外,如果你希望系统负载尽快降低,那么熔断要优于降级。
从具体实践上来说,降级可以玩出的花样要比熔断多很多。毕竟熔断是彻底不提供服务,而降级则是尽量提供服务。
降级分类
- 跨服务降级,当资源不够的时候可以暂停某些服务,将腾出来的资源给其他更加重要、更加核心的服务使用。这种策略的要点是必须知道一个服务比另外一个服务更有业务价值,或者更加重要。
- 本服务提供有损服务,例如各大 App 的首页都会有降级的策略。在没有触发降级的时候,App 首页是针对你个人用户画像的个性化推荐。而在触发了降级之后,则可能是使用榜单数据,或者使用一个运营提前配置好的静态页面。这种策略的要点是你得知道你的服务调用者能够接受什么程度的有损。
亮点方案
读写服务降级写服务
这个案例的基本思路是如果你的某个服务是同时提供了读服务和写服务,并且读服务明显比写服务更加重要,那么这时候你就可以考虑降级写服务。
我在公司维护了一个服务,它的接口可以分成两类:一类是给 B 端商家使用的录入数据的接口,另外一类是给 C 端用户展示这些录入的数据。所以从重要性上来说,读服务要比写服务重要得多,而且读服务也是一个高并发的服务。于是我接入了一个跨服务的降级策略。当我发现读服务的响应时间超过了阈值的时候,或者响应时间开始显著上升的时候,我就会将针对 B 端商家用户的服务临时停掉,腾出来的资源都给 C 端用户使用。对于 B 端用户来说,他们这个阶段是没有办法修改已经录入的数据的。但是这并不是一个特别大的问题。当 C 端接口的响应时间恢复正常之后,会自动恢复 B 端商家接口,商家又可以修改或者录入数据了。
虽然整体来说写服务 QPS 占比很低,但是对于数据库来说,一次写请求对性能的压力要远比一次读请求大。所以暂停了写服务之后,数据库的负载能够减轻不少。
升华
这个方案就是典型的跨服务降级。跨服务降级可以在大部分合并部署的服务里面使用,一般的原则就是 B、C 端合并部署降级 B 端;付费服务和非付费服务降级非付费服务。当然也可以根据自己的业务价值,将这些部署在同一个节点上的服务分成三六九等。而后在触发降级的时候从不重要的服务开始降级,将资源调配给重要服务。
快慢路径降级慢路径
使用缓存基本上都是先从缓存里面读数据,如果缓存里面没有数据,就从数据库中读取。那么在触发降级的情况下,你可以考虑只从缓存里面读取。如果缓存里面没有数据,那么就直接返回,而不会再去数据库里读取。这样可以保证在缓存里面有数据的那部分请求可以得到正常处理,也就是提供了有损服务。
我还用过另外一个降级方案。正常来说在我的业务里面,就是查询缓存,如果缓存有数据,那么就直接返回。如果缓存没有,那么就需要去数据库查询。如果此时系统的并发非常高,那么我就会采取降级策略,将请求标记为降级请求。降级请求只会查询缓存,而不会查询数据库。如果缓存没有,那就直接返回错误。这样能够有效防止因为少部分请求缓存未命中而占据大量系统资源,导致系统吞吐量下降和响应时间显著升高。
升华
这种思路其实可以在很多微服务里面应用。如果一个服务可以分成快路径和慢路径两种逻辑,那么在降级之前就可以先走快路径,再走慢路径。而触发了降级之后,就只允许走快路径。在前面的例子里面,从缓存里加载数据就是快路径,从数据库里面加载数据就是慢路径。
慢路径还可以是发起服务调用或者复杂计算。比如说一个服务快路径是直接查询缓存,而慢路径可能是发起很多微服务调用,拿到所有响应之后一起计算,算出来一个结果并缓存起来。那么在降级的时候,可以有效提高吞吐量。不过这种吞吐量是有损的,毕竟部分请求如果没有在缓存中找到数据,那么就会直接返回失败响应。
此文章为9月Day24学习笔记,内容来源于极客时间《后端工程师的高阶面经》