Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > go-zero组件Breaker

go-zero熔断机制组件Breaker接口定义使用解析

作者:Keson

这篇文章主要为大家介绍了go-zero熔断机制组件Breaker接口定义使用解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

概述

熔断机制是一种微服务保护机制。当某个微服务出现故障或者异常时,通过熔断机制,可以防止故障或异常扩散到整个微服务系统中,从而避免整个微服务系统被拖垮,熔断机制的大致流程是(以下流程基于 go-zero 熔断器为背景):

go-zero 熔断器时序图

源码解析

在 go-zero 中,Breaker 是一个接口,其接口定义如下:

Breaker interface {
   Name() string
   Allow() (Promise, error)
   Do(req func () error) error
   DoWithAcceptable(req func () error, acceptable Acceptable) error
   DoWithFallback(req func () error, fallback func (err error) error) error
   DoWithFallbackAcceptable(req func () error, fallback func (err error) error, acceptable Acceptable) error
}

Breaker 接口定义了 6 个方法,提供了 2 中对熔断器控制执行的机制:

Allow 方法抛出了一个 Promise 句柄,允许用户自行对熔断指标进行控制,采集指标可以根据用户需求选择请求时延、错误码等,且熔断触发后需要执行的逻辑也有用户自行控制。如 go-zero
中 rest 中熔断中间件的用法:

func BreakerHandler(method, path string, metrics *stat.Metrics) func(http.Handler) http.Handler {
 brk := breaker.NewBreaker(breaker.WithName(strings.Join([]string{method, path}, breakerSeparator)))
 return func(next http.Handler) http.Handler {
     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
         promise, err := brk.Allow()// 熔断执行逻辑,用户自行控制
         if err != nil {
             metrics.AddDrop()
             logx.Errorf("[http] dropped, %s - %s - %s",
                 r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent())
             w.WriteHeader(http.StatusServiceUnavailable)
             return
         }
         cw := &response.WithCodeResponseWriter{Writer: w}
         defer func() {// 熔断指标采集,用户自行控制
             if cw.Code < http.StatusInternalServerError {
                 promise.Accept()
             } else {
                 promise.Reject(fmt.Sprintf("%d %s", cw.Code, http.StatusText(cw.Code)))
             }
         }()
         next.ServeHTTP(cw, r)
     })
 }
}

DoXxx 方法在底层逻辑最终都是调用一个基础方法,只是根据入参的不同而暴露了不同的函数:

doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error

我们来看一下 doReq 方法的一个流程图:

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()
    if acceptable(err) {
        b.markSuccess()
    } else {
        b.markFailure()
    }
    return err
}

熔断器状态判断

判断熔断器是否开启是根据当前窗口的历史指标来计算的,这里采用了 google SRE 里面的丢弃比例算法,当丢弃比例为0时,则无需触发熔断,否则
随机触发熔断机制。

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
    }
    if b.proba.TrueOnProba(dropRatio) {
        return ErrServiceUnavailable
    }
    return nil
}
func (b *googleBreaker) history() (accepts, total int64) {
   b.stat.Reduce(func(b *collection.Bucket) {
      accepts += int64(b.Sum)
      total += b.Count
   })
    return
}

指标上报

指标上报是将用户执行后的请求错误返回给用户,用户可以根据错误码指定改错误码是否纳入失败指标

// 标记成功一次
func (b *googleBreaker) markSuccess() {
    b.stat.Add(1)
}
// 标记失败一次
func (b *googleBreaker) markFailure() {
    b.stat.Add(0)
}

总结

熔断器的实现起始就是一个指标采集+指标计算的过程,然后用户可以根据指标计算结果来决定是否触发熔断,其中难点还是在于指标统计过程,这个过程是用滑动窗口来进行指标采集的,关于滑动窗口算法这里就不做过多的介绍了。

以上就是go-zero组件Breaker的详细内容,更多关于go-zero 组件Breaker的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文