【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

分布式系统关注点:99% 人看得懂的“熔断”以及最佳实践

  • 2018-12-05
  • 本文字数:3190 字

    阅读完需:约 10 分钟

分布式系统关注点:99%人看得懂的“熔断”以及最佳实践

当我们工作所在的系统处于分布式系统初期的时候,往往这时候每个服务都只部署了一个节点。


那么在这样的背景下,如果某个服务 A 需要发布一个新版本,往往会对正在运行的其它依赖服务 A 的程序产生影响。甚至,一旦服务 A 的启动预热过程耗时过长,问题会更严重,大量请求会阻塞,产生级联影响,导致整个系统卡慢。



举个夸张的例子来形容:一幢楼的下水管是从最高楼直通到最低楼的,这个时候如果你家楼下的管道口堵住了,那么所有楼上的污水就会倒灌到你家。如果这导致你家的管道口也堵住了,之后又会倒灌到楼上一层,以此类推。


然而实际生活中一旦你发现了这个问题,必然会想办法先避免影响到自己家,然后跑到楼下让他们赶紧疏通管道。此时,避免影响自己家的办法就可被称之为「熔断」。


熔断本质上是一个过载保护机制。在互联网系统中的熔断机制是指:当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护自己以及系统整体的可用性,可以暂时切断对下游服务的调用。


做熔断的思路大体上就是:一个中心思想,分四步走。

一、熔断怎么做

首先,需秉持的一个中心思想是:量力而行。因为软件和人不同,没有奇迹会发生,什么样的性能撑多少流量是固定的。这是根本。


然后,这四步走分别是:


  1. 定义一个识别是否处于“不可用”状态的策略

  2. 切断联系

  3. 定义一个识别是否处于“可用”状态的策略,并尝试探测

  4. 重新恢复正常

定义一个识别是否处于“不正常”状态的策略

相信软件开发经验丰富的你也知道,识别一个系统是否正常,无非是两个点。


  • 是不是能调通

  • 如果能调通,耗时是不是超过预期的长


但是,由于分布式系统被建立在一个并不是 100%可靠的网络上,所以上述的情况总有发生,因此我们不能将偶发的瞬时异常等同于系统“不可用”(避免以偏概全)。由此我们需要引入一个「时间窗口」的概念,这个时间窗口用来“放宽”判定“不可用”的区间,也意味着多给了系统几次证明自己“可用”机会。但是,如果系统还是在这个时间窗口内达到了你定义“不可用”标准,那么我们就要“断臂求生”了。


这个标准可以有两种方式来指定。


• 阈值。比如,在 10 秒内出现 100 次“无法连接”或者出现 100 次大于 5 秒的请求。


• 百分比。比如,在 10 秒内有 30%请求“无法连接”或者 30%的请求大于 5 秒。


最终会形成这样这样的一段代码。


全局变量 errorcount = 0; //有个独立的线程每隔10秒(时间窗口)重置为0。全局变量 isOpenCircuitBreaker = false;
//do some thing...
if(success){ return success;}else{ errorcount++; if(errorcount == 不可用阈值){ isOpenCircuitBreaker = true; }}
复制代码
切断联系

切断联系要尽可能的“果断”,既然已经认定了对方“不可用”,那么索性就默认“失败”,避免做无用功,也顺带能缓解对方的压力。


分布式系统中的程序间调用,一般都会通过一些 RPC 框架进行。



那么,这个时候作为客户端一方,在自己进程内通过代理发起调用之前就可以直接返回失败,不走网络。



这就是常说的「fail fast」机制。就是在前面提到的代码段之前增加下面的这段代码。


if(isOpenCircuitBreaker == true){    return fail;}
//do some thing...
复制代码
定义一个识别是否处于“可用”状态的策略,并尝试探测

切断联系后,功能的完整性必然会受影响,所以还是需要尽快恢复回来,以提供完整的服务能力。这事肯定不能人为去干预,及时性必然会受到影响。那么如何能够自动的识别依赖系统是否“可用”呢?这也需要你来定义一个策略。


一般来说这个策略与识别“不可用”的策略类似,只是这里是一个反向指标。


• 阈值。比如,在 10 秒内出现 100 次“调用成功”并且耗时都小于 1 秒。


• 百分比。比如,在 10 秒内有 95%请求“调用成功”并且 98%的请求小于 1 秒。


同样包含「时间窗口」、「阈值」以及「百分比」。


稍微不同的地方在于,大多数情况下,一个系统“不可用”的状态往往会持续一段时间,不会那么快就恢复过来。所以我们不需要像第一步中识别“不可用”那样,无时无刻的记录请求状况,而只需要在每隔一段时间之后去进行探测即可。所以,这里多了一个「间隔时间」的概念。这个间隔幅度可以是固定的,比如 30 秒。也可以是动态增加的,通过线性增长或者指数增长等方式。


这个用代码表述大致是这样。


全局变量 successCount = 0; //有个独立的线程每隔10秒(时间窗口)重置为0。//并且将下面的isHalfOpen设为false。
全局变量 isHalfOpen = true;//有个独立的线程每隔30秒(间隔时间)重置为true。
//do some thing...if(success){ if(isHalfOpen){ successCount ++; if(successCount = 可用阈值){ isOpenCircuitBreaker = false; } } return success;}else{ errorcount++; if(errorcount == 不可用阈值){ isOpenCircuitBreaker = true; }}
复制代码


另外,尝试探测本质上是一个“试错”,要控制下“试错成本”。所以我们不可能拿 100%的流量去验证,一般会有以下两种方式:


  1. 放行一定比例的流量去验证。

  2. 如果在整个通信框架都是统一的情况下,还可以统一给每个系统增加一个专门用于验证程序健康状态检测的独立接口。这个接口额外可以多返回一些系统负载信息用于判断健康状态,如 CPU、I/O 的情况等。

重新恢复正常

一旦通过了衡量是否“可用”的验证,整个系统就恢复到了“正常”状态,此时需要重新开启识别“不可用”的策略。就这样,系统会形成一个循环。



这就是一个完整的熔断机制的面貌。了解了这些核心思想,用什么框架去实施就变得不是那么重要了,因为大部分都是换汤不换药。


上面聊到的这些可以说是主干部分,还有一些最佳实践可以让你在实施熔断的时候拿捏的更到位。

二、做熔断的最佳实践

什么场景最适合做熔断

一个事物在不同的场景里会发挥出不同的效果。以下是我能想到最适合熔断发挥更大优势的几个场景:


• 所依赖的系统本身是一个共享系统,当前客户端只是其中的一个客户端。这是因为,如果其它客户端进行胡乱调用也会影响到你的调用。


• 所以依赖的系统被部署在一个共享环境中(资源未做隔离),并不独占使用。比如,和某个高负荷的数据库在同一台服务器上。


• 所依赖的系统是一个经常会迭代更新的服务。这点也意味着,越“敏捷”的系统越需要“熔断”。


• 当前所在的系统流量大小是不确定的。比如,一个电商网站的流量波动会很大,你能抗住突增的流量不代表所依赖的后端系统也能抗住。这点也反映出了我们在软件设计中带着“面向怀疑”的心态的重要性。

做熔断时还要注意的一些地方

与所有事物一样,熔断也不是一个完美的事物,我们特别需要注意 2 个问题。


首先,如果所依赖的系统是多副本或者做了分区的,那么要注意其中个别节点的异常并不等于所有节点都存在异常,所以需要区别对待。


其次,熔断往往应作为最后的选择,我们应优先使用一些「降级」或者「限流」方案。因为“部分胜于无”,虽然无法提供完整的服务,但尽可能的降低影响是要持续去努力的。比如,抛弃非核心业务、给出友好提示等等,这部分内容我们会在后续的文章中展开。

三、总结

本文主要聊了熔断的作用以及做法,并且总结了一些我自己的最佳实践。


上面的这些代码示例中也可以看到,熔断代码所在的位置要么在实际方法之前,要么在实际方法之后。它非常适合 AOP 编程思想的发挥,所以我们平常用到的熔断框架都会基于 AOP 去做。


熔断只是一个保护壳,在周围出现异常的时候保全自身。但是从长远来看平时定期做好压力测试才能更好的防范于未然,降低触发熔断的次数。如果清楚的知道每个系统有几斤几两,在这个基础上再把「限流」和「降级」做好,这基本就将“高压”下触发熔断的概率降到最低了。


作者介绍:张帆(Zachary),7 年电商行业经验,5 年开发团队管理经验,4 年互联网架构经验,目前任职于某垂直电商技术总监。专注大型系统架构、分布式系统。坚持用心打磨每一篇原创。本文首发于公众号:跨界架构师(ID:Zachary_ZF)。


2018-12-05 14:405696

评论 2 条评论

发布
用户头像
学习了,谢谢
2019-01-10 09:23
回复
用户头像
受教了
2018-12-06 17:01
回复
没有更多了
发现更多内容

当 Redis 发生高延迟时,到底发生了什么

程序员历小冰

redis 延迟 28天写作 12月日更

银行兴起数字极简风:“智能手机App恐惧症”终于有救了

CECBC

Nginx+IIS做站点访问负载均衡

为自己带盐

nginx dotnet 28天写作 12月日更

一文带你了解数据库设计基础

坚果

数据库 28天写作 12月日更

2021百度AI开发者大会在元宇宙举办

百度大脑

人工智能

Fortinet :《2021 年OT与网络安全现状报告》 之「要点综述」

喀拉峻

网络安全

政法重点关注人员管控系统开发,跨部门大数据办案平台建设

a13823115807

Python代码阅读(第73篇):字符串字节数

Felix

Python 编程 字符串 阅读代码 Python初学者

Hadoop学习过程中遇到的错误及解决方法

阿丞

hadoop hdfs mapreduce YARN

个推漫话知识图谱:《女心理师》中的智能语音识别系统如何实现?

个推

机器学习 nlp 知识图谱

AI风起荆楚,人工智能中国体系即刻启航

脑极体

语音信号处理14:语音信号的特征应用

轻口味

28天写作 12月日更

如何促进用户首次下单?

石云升

AARRR 产品思维 28天写作 产品增长 12月日更

信通院首批AIOps系统和工具评估,博睿数据获评异常检测模块“全面级”

博睿数据

11月云短信报告出炉,腾讯云再次蝉联到达率冠军

博睿数据

Spring框架基础知识(01)

海拥(haiyong.site)

28天写作 12月日更

百度CTO王海峰:AI大生产平台再升级 助力中国科技自立自强

百度大脑

人工智能

【网络安全】针对 HTTP/2 协议的HTTP Desync攻击

H

网络安全 信息安全 漏洞

华为与湖北三所高校共建首批鲲鹏&昇腾产教融合育人基地

科技热闻

资产租赁管理系统解决方案

低代码小观

资产管理 CRM 企业管理系统 融资租赁 CRM系统

世界女性科技群落(四):技术与流行文化顶格发展,日韩女性的科技暗面

脑极体

基于kali的域控环境搭建——黑盒测试环境搭建

网络安全学海

黑客 网络安全 信息安全 渗透测试 WEB安全

Go 软件设计之道

宇宙之一粟

Go 语言 12月日更

研究了一圈React学习资料,发现最好用的React教程还是这个

sai

云原生促进基础设施变革,百度“磐玉”蜂巢服务器正式发布

科技热闻

年底了,聊聊述职

CatTalk

职场

Java 必看的 Spring 知识汇总!

CRMEB

忆父亲

wood

28天写作 父亲

设计消息队列存储消息数据的 MySQL 表格

奔奔

Hoo虎符研究院 | 币圈后浪——Swivel Finance

区块链前沿News

Hoo虎符 虎符交易所 虎符研究院

高性能队列Disruptor在测试中应用

FunTester

Disruptor 测试 性能测试 高性能队列 FunTester

分布式系统关注点:99%人看得懂的“熔断”以及最佳实践_架构_张帆_InfoQ精选文章