熔断原理与go代码实现

5 篇文章 1 订阅

在一个系统中,一个服务通常会依赖许多其他的服务,并且服务在某些时候失败是不可避免的。

如果我们失败的服务变得无响应,所有依赖它的服务也有变得无响应的风险。这就是所谓的 级联故障。

相关推荐

golang 实现时间滑动窗口_wangxiaoangg的博客-CSDN博客

应对过载-客户端限流算法_wangxiaoangg的博客-CSDN博客


一 熔断器原理

熔断器的基本思想非常简单。熔断器通过包装对目标服务的调用,来持续监控故障率。一旦故障达到某个阈值(阈值的设定可以参考文章:应对过载-客户端限流算法),熔断器将打开,并且所有进一步调用都返回故障或错误。

当一个服务变得无响应时,依赖它的服务应该停止等待,并开始处理失败逻辑。通过防止单个服务的故障导致整个系统中发生级联故障。

熔断器有三种状态:

  • closed:熔断器关闭。请求会被传递到目标服务端。熔断器继续监控错误率、请求数和超时等指标。当这些指标超过特定阈值时,断路器将跳闸并转换为open状态。

  • open:熔断器打开。请求不传递到目标服务端,而是fallback调用逻辑(由开发人员自行设定)来处理失败。熔断器会在open调用状态保持一段时间sleeping window,之后断路器可以从 open 过渡到 half-open

  • half-open:在此状态下,将有限数量的请求传递给目标服务端,用了探测目标服务器的可用状态是否恢复。如果目标服务可以成功响应,则 reset 中断返回 closed 状态。否则熔断器将转换回open状态。

二 go-zero中的熔断器实现

go zero中熔断器的实现参考了Google Sre过载保护算法;代码路径为go-zero/core/breaker。

1.googleBreaker结构体

const (
	// 250ms for bucket duration
	window     = time.Second * 10
	buckets    = 40
	k          = 1.5
	protection = 5
)

// googleBreaker is a netflixBreaker pattern from google.
// see Client-Side Throttling section in https://landing.google.com/sre/sre-book/chapters/handling-overload/
type googleBreaker struct {
	k     float64   //k用于调节 熔断器敏感度,默认1.5 
	stat  *collection.RollingWindow //时间滑动窗口,用于记录过去一段时间,请求数和接受数
	proba *mathx.Proba //随机数
}
  • 属性K,用于调节 熔断器敏感度,默认1.5 。K值越小 熔断器越敏感
  • stat,时间滑动窗口,用于记录过去一段时间 系统的请求数和成功数。(滑动窗口实现可参考golang 实现时间滑动窗口_wangxiaoangg的博客-CSDN博客

    默认 滑动窗口时间10s,样本窗口时间250ms

2. accept()

accept()方法用于判断是否要要丢弃当前请求

func (b *googleBreaker) accept() error {

   //获取当前总的发送数和接受数
   accepts, total := b.history()
   weightedAccepts := b.k * float64(accepts)
   // https://landing.google.com/sre/sre-book/chapters/handling-overload/#eq2101
   dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1))
   if dropRatio <= 0 {
      return nil
   }

   //dropRatio丢弃请求的概率,当生成的随机数小于 dropRatio时,则不发送请求到服务端
   if b.proba.TrueOnProba(dropRatio) {
      return ErrServiceUnavailable
   }

   return nil
}

3.doReq()

发送请求到服务端。

首先通过accept校验是否触发熔断,触发熔断时,直接调用失败逻辑。

func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error {
	
    //判断是否要丢弃请求
    if err := b.accept(); err != nil {
		if fallback != nil {
			return fallback(err)
		}

		return err
	}

	defer func() {
		if e := recover(); e != nil {
			b.markFailure()
			panic(e)
		}
	}()

    //发送请求
	err := req()

    //请求成功 滑动窗口accepts +1
	if acceptable(err) {
		b.markSuccess()
	} else {
		b.markFailure()
	}

	return err
}

4.计数相关

markSuccess:总的请求数+1,accepts +1

markFailure:总的请求数+1,accepts +0

history:获取窗口内总的请求数,和成功的请求数

func (b *googleBreaker) markSuccess() {
   b.stat.Add(1)
}

func (b *googleBreaker) markFailure() {
   b.stat.Add(0)
}

func (b *googleBreaker) history() (accepts, total int64) {
   b.stat.Reduce(func(b *collection.Bucket) {
      accepts += int64(b.Sum)
      total += b.Count
   })

   return
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值