微服务之容错

前言

微服务虽然带来了很多好处,但是引入的麻烦也不小。例如:分布式事务,分布式JOB,分布式SESSION等等。今天定个小目标,争取每周学习其中一个小题目。今天先谈谈容错。

微服务的容错

在微服务架构中,整个系统被拆分为多个独立部署的服务单元。各个服务模块通过rpc调用来完成整个系统的服务功能。那么,对于单一服务而言,不可避免的要面对这个问题:RPC调用失败了怎么办?而导致这个问题的原因可能有:

  1. 数据问题,导致下游系统失败了。
  2. 网络出现抖动,导致断线了。
  3. 发生网络分区了。
  4. 下游系统挂了。
    如果说,第一种情况,我们属于业务系统的问题。那么,我们应当如何应对后面三种情况呢?如果有同学说,这个事情发生的概率太低了,问题不大。那么一旦发生,后果将会非常严重,整个系统服务不可用!感兴趣的同学,可以自行了解一下,灾难级杀手:服务雪崩。为此,我们必须考虑服务容错。

容错手段

在RPC调用中,最简单且有效的容错手段就是超时。超时后的处理,也是对当前调用的一种降级。但是,如果服务提供者挂了,至少在重启之前,调用都会失败。因此,这时候就上断路器。在RPC调用满足一定程度的错误阈值(例如:在一段时间内发生错误的调用的占比)后,直接停止调用,直接降级处理。而在服务层面,为了保证负载在服务的承受范围内,采取限流措施。当然,也可以通过提高系统的承受能力,从而可以处理更多的请求。例如:引入缓存。

限流

我们每个服务器的处理能力都是存在极限的。只要网络并发请求超过了服务的处理极限,那么必然会把服务压垮。因此,限流是确保服务的负载在可承受可处理范围的一个重要手段。

限流算法

计算器算法

这是最简单的限流算法,也叫固定时间窗口算法。要理解这个算法,首先得明白,什么是固定时间窗口。例如,假定时间窗口长度为1分钟,那么从此刻开始,[now, now + 1)即为当前计数的时间窗口区间。在这个时间范围内的每一次请求,计数器都会+1,当达到设定的最大请求数阈值时,拒绝请求。只有等到下一个时间窗口,重置请求计数器后,才能继续请求。
微服务之容错_第1张图片
优点:简单粗暴、通俗易懂。
缺点:存在瞬时流量峰值问题。
例如,一个5秒时间窗口,在第5秒时来了100个请求(假设限流阈值为100request/5s),被限流了。但是在第6秒时,重置了请求计数,这是又来了50个请求,被放行了。超出了系统100request/5s的能力。

滑动时间窗口算法

针对计数器算法的缺点进行改进。既然一个时间窗口存在统计精度问题,那就多分几个小的时间窗口。同时,每次统计,向前滑动一个小时间串口(后面称格子)。从而实现在整个大的时间窗口内的请求数量不会超过阈值。下图例子为:时间窗口为5s,分5个小格子进行统计,即:每个小格子为1s。每次向前滑动一个小格子,即向前滑动一秒。
微服务之容错_第2张图片

优点:相比于计数器算法,解决了临界突发问题。
缺点:无法解决瞬时流量整个时间窗口范围内均会被限流,可能造成系统能力浪费(突刺现象)。例如:以上述为例,在第5秒瞬间100个请求打满。那么在接下来的5秒内均无法再有请求进来,而可能到第4秒的时候,已经全部处理完了。

漏桶算法

为了应对突刺现象,对请求流量进行整型,于是漏桶算法出现了。漏桶算法能够让流量以一个稳定速度流向后端服务。
微服务之容错_第3张图片

核心思想是,参照漏桶,让请求像漏桶漏水的流速一样,平滑的请求后端服务。一个比较容易理解的例子:消息队列。消息队列的容量就是水桶的容量,消息生产者就是水龙头,进水速度总是不固定。消息的消费者就是漏桶的漏洞,总是以固定速率消费消息。再举个例子,在通讯时,双方约定,只能以固定的速率向对方发送数据。因此请求方增加了一个缓存。应用漏桶的概念就是,缓存就是漏桶的大小。所有的需要发送的数据都往缓存中放,而另外有一个线程,专门以固定的速率向对方发送数据。
由此可见,通常漏桶算法都是异步的。

特点:

  1. 强制将请求以一个固定的速率流出。
  2. 削峰填谷,禁止突发。

令牌桶算法

先看下原理:
微服务之容错_第4张图片
核心思路:

  1. 固定速率往桶中放令牌。如果桶已满,则丢弃。
  2. 每个请求进来,都需要从桶中取令牌,成功则可以执行,否则拒绝执行。

举个例子,某服务器并发能力是10QPS,对应的令牌桶容量为10个令牌。假定处理能力是5个请求每秒,那么不妨设计令牌产生的速度为5个每秒。初始时,桶中有10个令牌,假设有10个并发,那么此时就会把桶中的令牌全部取走。下一个请求进来,得看具体的实现怎么处理了,RateLimiter的默认方法acquire是由是等待新的令牌产生,然后返回。这意味着至少需要等待1/5秒新的令牌产生才能继续获取到令牌。而tryAquire则不会等待,直接拒绝。而Sentinel也是直接拒绝的。

特点:

  1. 限制平均流速,允许一定程度的突发。(可以瞬时取到多个令牌,发出请求,只限制平均流速。)

应用:

  • 预热
    系统刚启动的时候,由于各种原因(缓存还没准备好、JVM热点代码未编译等等)处理能力还没有达到稳定状态,因此需要先预热。通过平滑的改变令牌产生的速度,逐步让系统达到稳定的水平。Sentinel预热的源码分析

限流的维度

  1. 限制总并发数
    如:线程池。弊端:线程切换开销 + 可能存在线程切换后的可能存在上下文丢失(ThreadLocal参数)。好处,通过线程隔离来保护原线程。
  2. 限制时间窗口内的平均速率-QPS。
    上面提到的限流算法,都属于这个范畴。

后记

可算是写完了,从去年写到今年。。花了很多时间来理解这个令牌桶跟漏桶算法。刚刚家人问,你还在看漏桶啊?才恍然发现。。但好在总算是理解了。二者的区别还是很大的。也明白了。
为什么说令牌桶才能允许突发?
因为令牌桶只要桶中有足够的令牌,可以同时允许多个请求流出。而漏桶,不管多少个请求进来,哪怕就是你进入到了漏桶中,最终还是只能等待调度,而调度总是以固定的速率流出的。
参考了很多sentinel官方的材料,下次好好聊聊sentinel。

参考

https://www.cnblogs.com/xuwc/p/9123078.html
https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5
https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
https://github.com/alibaba/Sentinel/wiki/Guideline:-%E4%BB%8E-Hystrix-%E8%BF%81%E7%A7%BB%E5%88%B0-Sentinel
https://github.com/alibaba/Sentinel/wiki/Sentinel-%E4%B8%8E-Hystrix-%E7%9A%84%E5%AF%B9%E6%AF%94

你可能感兴趣的:(微服务,分布式,java)