这位 Gopher 你好呀!如果觉得我的文章还不错,欢迎一键三连支持一下!文章会定期更新,同时可以微信搜索【凑个整数1024】第一时间收到文章更新提醒⏰
什么是断路器?
断路器(circuit-breaker)是软件系统弹力设计中常用的故障保护机制,提高软件系统的容错能力。断路器的工作方式类似于我们生活中所接触到的电路断路器,或者说是电闸上的“保险丝”,当电路出现问题(如短路)时会自动跳闸,保险丝会“熔断”,这时电路断开,可以起到保护电路上电器的作用,否则轻则导致电器烧坏,重则引发火灾,因此在电路的设计中,断路器这种自我保护装置可以说是不可或缺的。
同样,在计算机的场景中,一个服务可能会发生故障,当故障的严重程度达到某一阈值时,断路器会自动打开,以阻止对该服务的进一步调用,并直接返回一个预先设定好的错误响应,进而避免故障的加剧与扩散,提高系统的容错能力。
断路器同时还需要具备自我修复的能力。当故障发生一段时间后,断路器会尝试关闭,重新恢复对服务的调用,如果调用恢复正常,则断路器会关闭,否则继续打开,直到服务恢复正常。这就类似于我们生活中的电闸,当电路故障排除后,可以手动合闸,尝试恢复电路的通电状态。
断路器一般存在三种状态:闭合(closed)、断开(open)与半开(half-open),这与电路中的术语就很类似。断路器处于闭合状态时,服务正常,用户请求正常执行,断路器将不会对请求进行拦截;当客户请求因故障导致失败,且失败次数达到一定阈值时,断路器会自动打开并进入断开状态,此时断路器会拦截所有用户请求,并返回预先设定好的错误响应;断路器不可能永远处于断开状态,在一段时间后,断路器会尝试关闭,进入半开状态,重新尝试处理用户请求。如果请求处理成功的次数或比率达到一定阈值,则认为服务已经恢复正常,断路器会进入闭合状态,否则继续保持断开。
使用 Go 语言实现断路器
本文将以 Go 语言开源项目go-resiliency中的断路器实现为例,介绍使用 Go 语言的断路器代码设计与实现。go-resiliency 项目中的断路器实现位于breaker
包中,其实现了一个精炼好用的断路器,知名开源 Go 语言 Apache Kafka 客户端sarama项目中就依赖了 go-resiliency 中所实现的断路器。
断路器的基础使用
go-resiliency 的断路器提供了一个New
方法用于初始化一个断路器(breaker.Breaker
类型)实例:
func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker
输入参数分别为:
errorThreshold
:错误阈值,当timeout
时间内连续发生错误的次数达到该阈值时,断路器会从闭合状态转为断开状态;successThreshold
:成功阈值,当处于半开状态的断路器连续成功处理请求的次数达到该阈值时,断路器会重新闭合;timeout
:超时时间,断路器在超时时间内连续发生错误的次数达到errorThreshold
时,会从闭合状态转为断开状态;同时该超时时间也是断路器断开状态下的持续时间,当到期后,断路器会变为半开状态。
随后我们可以通过 breaker
实例的 Run
方法来执行需要断路保护的目标代码块。Run
方法所接收的目标函数类型为func() error
,即执行完后会返回一个error
,如果执行成功返回的就是nil
。下面我们来看一个简单的 demo:
func main() {
cnt := 0
work := func() error {
if cnt++; cnt >= 3 {
// 模拟从第三次调用开始出错
return errors.New("work got an error")
}
return nil
}
b := breaker.New(5, 1, 5*time.Second)
for range 10 {
switch err := b.Run(work); err {
case nil:
fmt.Println("work success!")
case breaker.ErrBreakerOpen:
fmt.Println(err