作者:@daemon365
本文为作者原创,转载请注明出处:https://www.cnblogs.com/daemon365/p/15231530.html
为什么要用熔断
前面我们讲过限流保证服务的可用性,不被突如其来的流量打爆。但是两种情况是限流解决不了的。
- 如果我们服务只能处理1000QPS,但是有10wQPS打过来,服务还是会炸。因为拒绝请求也需要成本。
- 服务但是io型的,会把mysql,redis,mq等中间件打挂。
所以,我们遵循一个思路,可不可以client端在失败的多的时候就不调用了,直接返回错误呢?
什么是熔断
熔断器是为了当依赖的服务已经出现故障时,主动阻止对依赖服务的请求。保证自身服务的正常运行不受依赖服务影响,防止雪崩效应。
源码分析
源码地址
CircuitBreaker 接口
| type CircuitBreaker interface { |
| Allow() error |
| MarkSuccess() |
| MarkFailed() |
| } |
- Allow()
- MarkSuccess()
- MarkFailed()
Group 结构体
| type Group struct { |
| mutex sync.Mutex |
| val atomic.Value |
| |
| New func() CircuitBreaker |
| } |
mutex
val
- map,存储name -> CircuitBreaker
New
Get方法
| |
| func (g *Group) Get(name string) CircuitBreaker { |
| m, ok := g.val.Load().(map[string]CircuitBreaker) |
| if ok { |
| breaker, ok := m[name] |
| if ok { |
| return breaker |
| } |
| } |
| |
| g.mutex.Lock() |
| nm := make(map[string]CircuitBreaker, len(m)+1) |
| for k, v := range m { |
| nm[k] = v |
| } |
| breaker := g.New() |
| nm[name] = breaker |
| g.val.Store(nm) |
| g.mutex.Unlock() |
| return breaker |
| } |
Breaker 结构体
| |
| type Breaker struct { |
| stat window.RollingCounter |
| r *rand.Rand |
| |
| randLock sync.Mutex |
| |
| |
| |
| k float64 |
| request int64 |
| |
| state int32 |
| } |
- stat
- r
- randLock
- k 成功系数
- request 请求数
- state
Allow()方法
| |
| func (b *Breaker) Allow() error { |
| success, total := b.summary() |
| k := b.k * float64(success) |
| |
| |
| if total < b.request || float64(total) < k { |
| if atomic.LoadInt32(&b.state) == StateOpen { |
| atomic.CompareAndSwapInt32(&b.state, StateOpen, StateClosed) |
| } |
| return nil |
| } |
| if atomic.LoadInt32(&b.state) == StateClosed { |
| atomic.CompareAndSwapInt32(&b.state, StateClosed, StateOpen) |
| } |
| dr := math.Max(0, (float64(total)-k)/float64(total+1)) |
| drop := b.trueOnProba(dr) |
| |
| |
| |
| if drop { |
| return circuitbreaker.ErrNotAllowed |
| } |
| return nil |
| } |
| |
| func (b *Breaker) trueOnProba(proba float64) (truth bool) { |
| b.randLock.Lock() |
| truth = b.r.Float64() < proba |
| b.randLock.Unlock() |
| return |
| } |
使用trueOnProba的原因是,当熔断器关闭时,随机让一部分请求通过,当success越大,请求的通过的数量就越多。用这些数据成功与否,放入窗口统计,当成功数达到要求时,就可以关闭熔断器了。
MarkSuccess()以及MarkFailed()方法
| |
| func (b *Breaker) MarkSuccess() { |
| b.stat.Add(1) |
| } |
| |
| |
| func (b *Breaker) MarkFailed() { |
| |
| |
| b.stat.Add(0) |
| } |
流程图



【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?