微服务领域里有个词叫服务熔断,你知道这是啥不?
故事要从我读大学那会说起。
因为功率问题,很多寝室都是不让用吹风筒和热水壶的。
但我那时候头铁,不仅用,而且还同时开了两个热水壶和一个吹风筒。直接给寝室电路来了个压测。
不出意外的出了意外,寝室直接停电。
一时间,隔壁寝室灯火通明,我们寝室一片漆黑。
作为本科专业电气工程的靓仔,我们意识到,这妥妥是电路过载导致断路器跳闸了。
于是我们趁社管阿姨不注意,偷偷摸进配电房,手动将断路器开关复位,寝室就来电了。
是真的有惊无险。
如果没有这个断路器,寝室总电路怕是得因为过载全部烧掉,我们几个妥妥会提前进入社会大学。
我能毕业,全靠这个断路器!
看到这里,我们知道了断路器的作用,就是在电路出问题的时候及时断开电路,避免过载,从而保护电路。
在微服务领域,我们也可以借鉴断路器的思路,引入了服务熔断的概念。
服务熔断是什么
服务熔断,也就是 Circuit Breaker,本质上是一种软件设计模式,用于在分布式系统中处理服务调用失败的情况。
假设有个 A 服务调用 B 服务的场景,如果 B 服务已经出现频繁失败的情况,A 继续调用只会加剧 B 服务的负担,严重的时候,有可能导致 B 服务崩溃,甚至出现 B 服务重启后立马被打崩的情况。因此,最好的做法是,在一段时间内先不要再频繁调用 B 服务。
为了实现这个保护效果,我们可以在 A 和 B 之间加一个熔断器。当 B 服务频繁失败时,熔断器可以防止 A 继续频繁调用 B 服务,相当于阻断服务间的请求,并且还能在 B 服务恢复正常之后,恢复 A 对 B 的调用。
熔断器的作用
工作原理也和上文提到的宿舍电路里的断路器类似。当服务调用失败的次数超过某个阈值时,熔断器会自动“打开”(Open),阻止进一步的服务调用,防止不断报错重试导致压垮被调用服务。
然后在在一段时间之后,熔断器开始尝试允许少量的请求通过,以检查服务是否已经恢复,也就是所谓的“半打开”(HalfOpen)。
如果这些请求成功,熔断器会“关闭”(Close),系统恢复正常的服务调用;但如果调用还是失败,那熔断器会继续再次回到“打开”(Open)状态。
上面提到的三个状态Open,HalfOpen和Close是服务熔断中非常重要的三个状态。
- Closed(关闭):这是熔断器的初始状态。在这种状态下,可以进行服务间调用,熔断器会跟踪服务调用的成功和失败情况。如果失败调用次数,到了某个配置的阈值,熔断器就会切换到 Open(打开)状态。
熔断器关闭
- HalfOpen(半开):保持 Open 状态一段时间后,熔断器会尝试进入 HalfOpen 状态。这个状态下,熔断器会尝试放几个请求通过,看下被调用服务是否已经恢复。如果这些请求成功,熔断器就会回到 Closed 状态;如果失败,那它会退回到 Open 状态。
熔断器半打开
- Open(打开):当熔断器检测到服务调用连续失败时,它会切换到 Open 状态。在这种状态下,熔断器会阻止所有对服务的调用,直到超时时间过后,或者在 HalfOpen 状态下的探测请求成功。
熔断器打开
它们的状态流转关系就像下图这样。
熔断状态机
怎么使用熔断器?
可以看出,熔断器的逻辑其实很简单,而且这么通用的功能,必然有现成的库可以直接拿来用。
比如阿里开源的sentinel-golang。
使用也比较简单。只需要三步。
1.引入 circuitbreaker 库
"github.com/alibaba/sentinel-golang/core/circuitbreaker"
2.声明熔断规则
通过circuitbreaker.LoadRules加载对应的熔断规则。
_, err = circuitbreaker.LoadRules([]*circuitbreaker.Rule{
// Statistic time span=5s, recoveryTimeout=3s, maxErrorRatio=40%
{
Resource: "api_url",
Strategy: circuitbreaker.ErrorRatio,
RetryTimeoutMs: 3000,
MinRequestAmount: 10,
StatIntervalMs: 5000,
StatSlidingWindowBucketCount: 10,
Threshold: 0.4,
},
})
这里面有几个需要注意的地方:
- Resource 是想要保护的资源名称,也就是上面提到的 B 服务,这里可以直接使用被调用方的 url。
- Strategy 是指熔断策略,示例代码里展示的是错误率,也就是说服务达到 xx 比例的错误率时就会触发熔断。同时这里还支持填其他策略,比如从错误率换成错误次数或者是慢调用的比例个数。
- RetryTimeoutMs 是指熔断器打开后经过多长时间后进行重试。在熔断器 Open 期间,请求会被直接拒绝,不会发送到后端 Resource(B 服务)。在指定的超时时间之后,熔断器将尝试发送一个请求以检查后端资源的可用性。
- MinRequestAmount 表示在进行熔断之前必须满足的最小请求数量。只有当请求的数量达到或超过这个阈值时,熔断器才会生效。这个参数可以用来避免在系统启动时就触发熔断。
- StatIntervalMs 表示统计信息的时间间隔,以毫秒为单位。在这个时间间隔内,熔断器将收集请求的统计信息,用于计算错误率。
- Threshold:表示错误率的阈值。当错误率超过这个阈值时,熔断器将触发熔断,停止发送请求到后端资源。
3.加入熔断保护
在需要进行熔断保护的地方,加入下面的代码:
e, b := sentinel.Entry("api_url")
if b == nil {
// 通过检测,不需要熔断,直接执行api调用
err := api_call()
if err != nil {
sentinel.TraceError(e, err)
}
// 保证执行完之后退出资源
e.Exit()
}
上面的 sentinel.Entry()方法内部会自动检测"api_url"这个资源是否需要打开熔断器,如果 api 调用报错了,可以通过 sentinel.TraceError 记录下来,sentinel 内部会根据报错去计算报错率,自动判断要不要熔断。
到这里,就算使用上熔断器的能力啦。
总结
- 服务熔断是一种软件设计模式,用于分布式系统中处理服务调用失败的情况,可以防止被调用服务因为频繁失败被压垮。它借鉴了电路中的断路器原理,通过监控服务调用的失败率等条件来决定是否阻止进一步的调用,以保护系统免受过载。
- 服务熔断器有三个主要状态:关闭(Closed)、半开(HalfOpen)和打开(Open),分别对应不同的保护策略。当服务调用失败次数超过阈值时,熔断器打开,阻止服务调用。在一定时间后,熔断器尝试半开状态,允许少量请求通过以测试服务恢复情况。如果服务恢复,熔断器关闭;如果失败,熔断器保持打开状态。
- 在 go 语言里可以使用 sentinel-golang 库实现熔断功能。