这是个人第 66 篇原创文章
git
做者 | 悟空聊架构github
来源 | 悟空聊架构(ID:PassJava666)web
滚滚长江东逝水,浪花淘尽英雄。是非成败转头空。青山依旧在,几度夕阳红。算法
-- 来自《三国演义》数据库
本篇将会经过三国中的赤壁之战
来说述周瑜、黄盖和诸葛亮是怎么把服务雪崩
玩到极致的。c#
本文已收录到个人 Github,点击文末的阅读原文打开。给个Star吧~缓存
https://github.com/Jackson0714/PassJava-Learning服务器
赤壁之战
话说东汉末年,曹操、孙权、刘备在长江赤壁(今湖北蒲圻西北)进行了一次争夺老大位置的大战,这就是有名的赤壁之战
。微信
1、还原赤壁之战
曹操统一北方后,南下战胜了刘备,占领荆襄之地后,还想干掉东边的孙权,因而刘备和孙权一块儿联合抗击曹军八十万大军。网络
曹操的军队大部分都是北方的,对于水上做战的经验很是欠缺,并且不少士兵晕船,因而曹操命令军队将船尾用铁索相连
,减弱了风浪颠簸,利于士兵演练。

咱们来看看周瑜、黄盖、诸葛亮的对话:

❝❞
黄盖
:曹操是真的蠢啊,把船连着,若是船烧着了,其余船会跟着一块儿烧着的。锁链不易解开,船都逃不了了。咱们用火攻,直接把曹军干趴下。周瑜
:但如何接近他们的船呢?黄盖
:我用诈降带几艘船出发,船上载浸油的干草,等接近曹军时,点燃干草,冲向曹军的连环船,引燃他们的船只。周瑜
:妙啊!但是哪来的东风?诸葛亮
:我来借东风~
赤壁之战那天,火船乘风闯入曹军船阵,顿时一片火海。联军乘势攻击,曹军伤亡惨重,最后以联军大胜结束,成为了以少胜多的经典战役。

2、战情分析
周瑜和黄盖看出了连环船的弱点:「若是一只船被烧着了,也会把连着的船烧着」 。
这就很像咱们的系统中出现的服务雪崩
问题。
假定咱们系统引进了微服务的思想,将多个服务进行拆分,每一个服务都是经过接口调用来完成的,看似功能经过微服务化后,功能和职责单一,正是咱们想要的.
但随着业务的增加,服务的数量也是随之增多
,逻辑也会更加复杂
,一个服务的某个逻辑须要依赖
多个其余服务才能完成。假如一个被依赖的服务不能向上游的服务提供服务,则极可能形成雪崩效应
,最后致使整个服务不可访问
。
就像雪山上某一处出现积雪崩塌的现象,慢慢地带动其余片区的积雪崩塌,产生了级联反应,最后形成大片的积雪崩塌,这就是常见的雪崩场景。
「小结」 一个服务失败,致使整条链路的服务都失败的场景,称为服务雪崩。
那曹军应该怎么避免这个问题呢?别急,后面再看答案。
3、系统中的雪崩效应
微服务之间每每采用 RPC 或者 HTTP 调用,通常都会设置调用超时的限制,或者经过失败重试机制来确保服务成功执行。但若是不考虑服务的熔断和限流,仍是很容易产生服务雪崩的。下面用例子来说解下雪崩效应是怎么产生的。

-
咱们系统中三个服务:
订单服务
、商品服务
、库存服务
。 -
下单场景:用户下单了一个商品,客户端调用订单服务来生成预付款订单,订单服务调用商品服务查看下单的哪款商品,商品服务调用库存服务判断这款商品是否有库存,若有库存,则能够生成预付款订单。
-
假定因双十一
流量暴增
,库存服务不可用(如响应超时等),库存服务收到的不少请求都未处理完,它将没法处理更多请求。 -
而上游的商品服务
依赖
库存服务,商品服务的超时
和重试机制
会被执行。商品服务新的调用不断产生,会致使商品服务的调用被大量积压
,产生大量的调用等待
和重试调用
,慢慢耗尽
商品服务的资源,好比内存,结果致使商品服务也宕机了。 -
而订单服务也会重走商品服务的老路。结果就是三个服务
都不可用了
。
4、形成雪崩的真实场景
1.4.1 服务提供者不可用
-
硬件故障:
如网络故障、硬盘损坏等。 -
程序的 bug:
如算法须要占用大量 CPU 的计算时间致使 CPU 使用率太高。 -
缓存击穿:
好比应用刚重启,短期内缓存是失效的,致使大量请求直接访问到了数据库,数据库不堪重负,服务不可用。 -
秒杀和大促:
服务短期承载不了那么多请求量。
1.4.2 重试加大流量
-
用户连续重试:
好比用户看到界面上没有响应,因此又操做了一遍,结果又增长了一倍请求量。 -
程序重试机制:
好比代码中有屡次重试的逻辑,一次失败后,过几秒后再重试,重试个三次就取消重试,走异常处理分支了。也是增长了请求量。
5、如何防止雪崩
方案
出问题前预防:限流、主动降级、隔离
出问题后修复:熔断、被动降级
「本篇主要来说解熔断机制。」 后续几篇会讲解其余方案。
6、熔断原理和算法
1.6.1 熔断概念

熔断这个概念来源于电路系统中的保险丝
熔断。当电流过大时,保险丝熔断,防止因电流过大
损坏电器元器件,或因电流过大,致使元器件热度太高,发生火灾。

「物理公式」 电功率 P = I^2 * R,I 表明电流,元器件的电阻 R 不变的状况下,电流越大,电功率约大,电阻作的电功大部分都用来发热
了,因此电功率越大,发热越严重。(还好高中物理没忘。)
放到咱们系统中,怎么理解熔断?
若是在某段时间内,调用某个服务很是慢甚至超时,就能够将这个服务熔断,后续其余服务再调用这个服务就直接返回,告诉其余服务:「“已经熔断了,你别调用我了,过段时间再来试下吧。”」
1.6.2 如何熔断
「熔断有个原则」 一段时间内,统计失败的次数或者失败请求的占比超过必定阈值,就进行熔断。
详细的原理以下图所示:

1.6.3 统计请求的算法
-
请求访问到后台服务后,首先判断熔断开关是否打开。
-
若是熔断开关
已打开
,则代表当前请求不能被处理。 -
若是熔断开关
未打开
,则判断时间窗口是否已满。 -
若是时间窗口
未满
,则请求桶中的请求数加 1。 -
若是返回的响应有异常,则失败桶的
失败数加 1
,若是返回的响应没有异常,则成功桶的成功数加 1
。 -
若是时间窗口
已满
,则开始判断是否须要熔断。
1.6.4 熔断的恢复算法
-
当熔断后,开关切换到
断开状态
。 -
过一段时间后,开关切换为
半断开状态
(Half-Open)。半断开状态下,容许对应用程序的必定数量的请求能够去调用服务,若是调用成功,则认为服务能够正常访问了,因而将开关切换为闭合状态
。 -
若是半断开状态下,仍是有调用失败的状况,则认为服务尚未恢复,开关从半断开状态切换到
断开状态
。
1.6.5 统计失败率的时间窗口

-
时间窗口能够比喻为人坐在窗户边,看外面来往的车辆,必定时间内从窗户外通过的车辆。
-
每次请求,都会判断时间窗口是否已满(如5分钟),若是时间窗口已满,则从新开始计时,且清理请求数/成功数/失败数。
-
注意:第一次开始的起始时间默认为当前时间。
1.6.6 尝试恢复服务的时间窗口

-
开关为断开的状态,通过必定时间后,好比 1 分钟,设置为
半断开
的状态,尝试发送请求检测服务是否恢复。 -
若是已恢复,则切换状态为关闭状态。若是未恢复,则切换状态为
断开
的状态,通过 1 分钟后,重复上面的步骤。 -
这里的时间窗口能够根据环境的运行状态进行动态调整,好比第一次是 1 分钟,第二次是 3 分钟,第三次是 10 分钟。
7、熔断中间件
确定有人会问了,你这上面讲的原理,难道还真的本身去写这套算法?
「答案:是的,项目中咱们本身造了一个轮子:熔断器。」
但这里我不推荐你们这么作。市面上还有更优秀的开源组件供你们使用,好比阿里系的 Sentinel
(推荐),Netflix 的 Hystrix
(已中止更新)。
固然 Sentinel 就不在这篇讲了,后续奉上~
8、扭转战局
曹操大败是由于连锁船的缘由,那如何给曹操提供一妙计
,助他扭转战局呢?
「方案有以下几个」
-
能够用麻绳代替锁链,因绳子更容易割断。(熔断机制)
-
将船划分到几个区域,区域之间保持必定距离,即便某个区域烧着了,也不会影响其余区域。(熔断+资源隔离)
-
在湖面上提早设关卡,黄盖过来的话,先检查船和人,有问题不予通行。(熔断)

9、限流、降级
原本是想在这篇把限流
和降级
也写完的,发现熔断的内容越写越多了,那就把限流和降级放在后面几篇吧。也是三国故事哦~
写在最后
《三国演义》也是我很是喜欢的一部文学做品,书大概看了 80 %,电视剧是看完了的。
最喜欢的角色固然是军师诸葛亮啦,还有枭雄曹操~~
- END -
我建了一个学习交流群「在群里不说话都能进步哦~」 扫码加我,备注[加群]
我的二维码
更多内容

我是一个秒杀请求,正在逃离这颗星球...

若是把四个消息队列都拉到一个群里,他们会聊些什么?

这三年被分布式坑惨了,曝光十大坑
点亮,服务器三年不宕机
本文分享自微信公众号 - 悟空聊架构(PassJava666)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。