1. 熔断
1.1 熔断来源
我们家用电闸上都有保险丝模块,当电压出现短路问题时,自动跳闸,此刻电路主动断开,我们的电器就会收到保护。否则,不能断开,后果不堪设想。
保险丝就是一个自我保护装置,保护整个电路。
1.2 分布式系统中的熔断
在分布式系统中,我们往往需要依赖下游服务,不管是内部系统还是第三方服务,如果下游出现问题,我们还是盲目地去请求,及时失败了多次,还是傻傻的去请求,去等待。
这样,
一是增加了整个链路的请求时间
第二,下游系统本身就出现了问题,不断的请求又把系统问题加重了,恢复困难。
1.3 熔断的作用
熔断模式可以防止应用程序不断地尝试可能超时和失败的服务,能达到应用程序执行而不必等待下游服务修正错误服务。
熔断器模式最牛的是能让应用程序自我诊断下游系统的错误是否已经修正,如果没有,不放量去请求,如果请求成功了,慢慢的增加请求,再次尝试调用。
2. 降级
2.1 降级的本质
降级就是为了解决资源不足和访问量增加的矛盾
在有限的资源情况下,为了能抗住大量的请求,就需要对系统做出一些牺牲,有点“弃卒保帅”的意思。放弃一些功能,保证整个系统能平稳运行
2.2 降级牺牲的是什么?
强强一致性变成最终一致性
大多数的系统是不需要强一致性的。
强一致性就要求多种资源的占用,减少强一致性就能释放更多资源
这也是我们一般利用消息中间件来削峰填谷,变强一致性为最终一致性,也能达到效果
干掉一些次要功能
停止访问不重要的功能,从而释放出更多的资源
举例来说,比如电商网站,评论功能流量大的时候就能停掉,当然能不直接干掉就别直接,最好能简化流程或者限流最好
简化功能流程。把一些功能简化掉。
2.3 降级的注意点
对业务进行仔细的梳理和分析
哪些是核心流程必须保证的,哪些是可以牺牲的
什么指标下能进行降级
吞吐量、响应时间、失败次数等达到一个阈值才进行降级处理
如何降级
降级最简单的就是在业务代码中配置一个开关或者做成配置中心模式,直接在配置中心上更改配置,推送到相应的服务。
3. 限流
3.1 限流的目的
限流顾名思义,就是对请求或并发数进行限制;通过对一个时间窗口内的请求量进行限制来保障系统的正常运行。如果我们的服务资源有限、处理能力有限,就需要对调用我们服务的上游请求进行限制,以防止自身服务由于资源耗尽而停止服务。
在限流中有两个概念需要了解。
- 阈值:在一个单位时间内允许的请求量。如 QPS 限制为 10,说明 1 秒内最多接受 10 次请求。
- 拒绝策略:超过阈值的请求的拒绝策略,常见的拒绝策略有直接拒绝、排队等待等。
3.2 限流有哪些行为
拒绝服务
最简单的方式,把多余的请求直接拒绝掉
做的高大上一些,可以根据一定的用户规则进行拒绝策略。
服务降级
降级甚至关掉后台的某些服务。
特权请求
在多租户或者对用户进行分级时,可以考虑让一些特殊的用户有限处理,其他的可以考虑干掉
延时处理
可以利用队列把请求缓存住。削峰填谷。
3.3 限流的实现方式
固定窗口算法-计算器法
最简单的实现方式 ,维护一个计数器,来一个请求计数加一,达到阈值时,直接拒绝请求。
一般实践中用 ngnix + lua + redis 这种方式,redis 存计数值
这种方式存在时间窗口临界突变问题
比如我们限制 QPS 为 2,但是当遇到时间窗口的临界突变时,如 1s 中的后 500 ms 和第 2s 的前 500ms 时,虽然是加起来是 1s 时间,却可以被请求 4 次。
滑动窗口算法
上图的示例中,每 500ms 滑动一次窗口,可以发现窗口滑动的间隔越短,时间窗口的临界突变问题发生的概率也就越小,不过只要有时间窗口的存在,还是有可能发生时间窗口的临界突变问题。
滑动日志算法
滑动日志算法是实现限流的另一种方法,这种方法比较简单。基本逻辑就是记录下所有的请求时间点,新请求到来时先判断最近指定时间范围内的请求数量是否超过指定阈值,由此来确定是否达到限流,这种方式没有了时间窗口突变的问题,限流比较准确,但是因为要记录下每次请求的时间点,所以占用的内存较多。
漏斗模式
漏桶算法中的漏桶是一个形象的比喻,这里可以用生产者消费者模式进行说明,请求是一个生产者,每一个请求都如一滴水,请求到来后放到一个队列(漏桶)中,而桶底有一个孔,不断的漏出水滴,就如消费者不断的在消费队列中的内容,消费的速率(漏出的速度)等于限流阈值。即假如 QPS 为 2,则每 1s / 2= 500ms
消费一次。漏桶的桶有大小,就如队列的容量,当请求堆积超过指定容量时,会触发拒绝策略。
下面是漏桶算法的示意图。
由介绍可以知道,漏桶模式中的消费处理总是能以恒定的速度进行,可以很好的保护自身系统不被突如其来的流量冲垮;
但是这也是漏桶模式的缺点,假设 QPS 为 2,同时 2 个请求进来,2 个请求并不能同时进行处理响应,因为每 1s / 2= 500ms
只能处理一个请求。
令牌桶
令牌桶算法同样是实现限流是一种常见的思路,最为常用的 Google 的 Java 开发工具包 Guava 中的限流工具类 RateLimiter 就是令牌桶的一个实现。令牌桶的实现思路类似于生产者和消费之间的关系。
系统服务作为生产者,按照指定频率向桶(容器)中添加令牌,如 QPS 为 2,每 500ms 向桶中添加一个令牌,如果桶中令牌数量达到阈值,则不再添加。
请求执行作为消费者,每个请求都需要去桶中拿取一个令牌,取到令牌则继续执行;如果桶中无令牌可取,就触发拒绝策略,可以是超时等待,也可以是直接拒绝本次请求,由此达到限流目的。
下面是令牌桶限流算法示意图。
思考令牌桶的实现可以以下特点。
- 1s / 阈值(QPS) = 令牌添加时间间隔。
- 桶的容量等于限流的阈值,令牌数量达到阈值时,不再添加。
- 可以适应流量突发,N 个请求到来只需要从桶中获取 N 个令牌就可以继续处理。
- 有启动过程,令牌桶启动时桶中无令牌,然后按照令牌添加时间间隔添加令牌,若启动时就有阈值数量的请求过来,会因为桶中没有足够的令牌而触发拒绝策略,不过如 RateLimiter 限流工具已经优化了这类问题。
3.4 限流的一些注意点
限流越早设计约好,架构成型后,不容易加入
限流模块不要成为系统的瓶颈,性能要求高
最好有个开关,可以直接介入
限流发生时,能及时发出通知事件
限流发生时,给用户提供友好的提示 。
4. 三者的关系
熔断强调的是服务之间的调用能实现自我恢复的状态;
限流是从系统的流量入口考虑,从进入的流量上进行限制,达到保护系统的作用;
降级,是从系统内部的平级服务或者业务的维度考虑,流量大了,可以干掉一些,保护其他正常使用;
熔断 < 降级 < 限流
三者都是为了通过一定的方式去保护流量过大时,保护系统的手段。