在网上商城点了付款按钮,屏幕却一直转圈,这背后究竟发生了什么?在如今众多服务互相调用、协同运转的软件中,一处故障很容易一连串地扩散开来。为了防止这种情况,暂时切断发往出问题之处的请求的设计方法,就叫作“断路器模式(Circuit Breaker Pattern)”。
简单来说,断路器模式指的是“自动拦下接连失败的调用,等对方恢复后再重新接通的安全开关”。这个名字来自电流过大时会自动跳闸的电路断路器。迈克尔·尼加德(Michael T. Nygard)在2007年出版的《Release It!》一书中介绍了这一模式,此后它广为人知。
本文将依次介绍断路器模式的由来、关闭·打开·半开三种状态、在实际系统中的应用,以及设计时需要注意的地方。
为了不让故障扩散而自行切断的开关
断路器模式的由来

一个应用的背后,往往有会员、商品、支付、配送等分工不同的多个服务,通过网络互相调用来完成工作。在这种结构中,只要有一个服务变慢,等待它的一方也可能跟着变慢。
(1) 一处故障蔓延开来的级联故障
如果支付服务无法响应,订单服务就会停下来等待回复,调用订单服务的界面一侧也会跟着卡住。这种故障像锁链一样环环相扣的现象,叫作级联故障(Cascading Failure)。
(2) 等待期间被占用的资源
每一个等待无响应服务的请求,都会占着线程、连接和内存。请求不断堆积,这些资源就会耗尽,连原本正常的功能也无法使用,而出故障的服务则因为蜂拥而至的重试请求,连恢复的机会都得不到。
(3) 借鉴自电路断路器的思路
电路断路器在电流过大时会切断电路,防止电线烧毁。尼加德把这一原理搬到软件中,提出在失败持续发生时直接切断调用本身。2014年马丁·福勒(Martin Fowler)撰文解读这一模式后,它在开发者之间传播得更广。
关闭、打开、半开三种状态

断路器是夹在调用方与被调用方之间的一个小型监视装置。它统计请求是成功还是失败,并按照事先设定的标准在三种状态之间切换,决定放行还是拦截请求。

- 关闭(Closed):平时的状态,请求照常通过,同时统计失败次数或失败率。
- 打开(Open):失败超过标准后进入的状态,不再调用对方,而是立即返回错误或替代响应。
- 半开(Half-Open):经过设定的时间后,只放行一部分请求作为试探,确认对方是否已经恢复。
- 快速失败(Fail Fast):不长时间等待,而是立刻告知失败,从而节省资源的方式。
(1) 统计失败的关闭状态
“关闭”这个名字来自电路接通、电流可以流动的状态。比如事先设定“最近100个请求中有一半以上失败”这样的标准,一旦越过这条线,就切换为打开状态。
(2) 不再等待的打开状态
在打开状态下,请求不会到达出故障的服务。用户不必长时间等待,可以马上得到提示;出故障的服务也摆脱了蜂拥而至的请求,获得恢复的时间。
(3) 谨慎确认的半开状态
打开状态持续到设定的时间后,就会进入半开状态,只放行少量请求。如果试探请求成功,就回到关闭状态;如果再次失败,就退回打开状态,重新开始等待。
在实际系统中的应用

断路器模式最早在大量服务相互咬合运转的大型互联网企业中扎根,如今已经可以通过面向多种编程语言的程序库轻松使用。
(1) Netflix的Hystrix
Netflix在2012年公开了带有断路器功能的Java程序库Hystrix。Hystrix在2018年停止了新功能开发,此后在Java领域Resilience4j、在.NET领域Polly被广泛使用。
(2) 填补空缺的降级响应
当电路打开时,系统可以不只显示错误,而是返回事先准备好的替代响应,也就是降级(Fallback)。例如,个性化推荐服务停止时,可以用热门列表代替个人列表,让页面不至于空白。
(3) 一起使用的其他安全装置
断路器常常与以下机制配合使用:超过设定时间就结束等待的超时(Timeout)、隔一段时间重新发送失败请求的重试(Retry),以及像船舱隔板一样划分资源、防止一侧故障波及另一侧的舱壁隔离(Bulkhead)。
设计时需要注意的地方

断路器并不是打开开关就万事大吉的装置。标准设得如何,决定了它是保护系统的盾牌,还是挡住正常功能的绊脚石。
(1) 既不过于敏感,也不过于迟钝
标准定得太低,稍有波动电路就会打开,连正常请求也被拦下;定得太高,则要等故障已经扩散后才会打开。因此,除了失败率之外,还要一并设定做出判断所需的最少请求数,以及把慢响应视为失败的时间标准。
(2) 确定哪些算作失败
用户输入错误的值而引发的错误并不是对方服务的故障,所以通常不计入。只有服务器错误、响应延迟这类能反映对方状态的失败才应计入,电路才不会莫名其妙地打开。
(3) 盯住状态的眼睛
电路何时打开、何时关闭,是提示故障的重要信号。把状态变化记录下来,并通过告警和仪表盘展示出来,运维人员就能迅速找出哪个服务出了问题。
说到底,断路器模式不是消灭失败的技术,而是把失败关起来的技术。与其抓着出故障的地方大家一起等,不如暂时切断、等它恢复——这个简单的想法,就是理解断路器模式的第一步。