熔断器模式

最近看其他人的代码时,发现其使用了github.com/sony/gobreaker包,了解了下,这个是熔断器模式的golang版的实现,下面就说说自己对这个模式的理解。

当我们调用其他服务时,如果失败一般会抛出并记录异常或者重试再调用几次,这个熔断器模式就是用于这个场景的。在一些场景中,如果服务暂时不可用,如果全做重试处理,可能会加重服务端的负担,并且重试多次也会导致自己的服务页面的不可用,慢慢不可用的状态会扩散到更多服务。熔断器模式通过记录最近多次的请求结果,来判断当前要调用的服务是否正常,不过失败次数过多,则下次再请求这个服务时,不再请求直接返回失败。当过一段时间后,再少量尝试调用,如果这几次调用服务都可以正常返回,则解除限制。

这个处理方法,类似电路上的保险丝,如果服务多次不能正常提供服务,则暂停再去请求服务,待过一段时候后(给服务恢复留些时间),再尝试看看服务是不是好了,如果没好,重复等待、尝试,好了的话,就跟正常调用一样。

下面我们github.com/sony/gobreaker中是如何实现这个熔断器的。

熔断器对象:

type CircuitBreaker struct {
    name          string
    maxRequests   uint32
    interval      time.Duration
    timeout       time.Duration
    readyToTrip   func(counts Counts) bool
    onStateChange func(name string, from State, to State)

    mutex      sync.Mutex
    state      State
    generation uint64
    counts     Counts
    expiry     time.Time
}

熔断器状态

我们先说几个比较重要的字段,首先是 state 字段。

// State is a type that represents a state of CircuitBreaker.
type State int

// These constants are states of CircuitBreaker.
const (
    StateClosed State = iota
    StateHalfOpen
    StateOpen
)

熔断器运行中,有三种状态,分别是闭合状态、断开状态和半断开状态。

  • 闭合状态:也是最开始的状态,这个是时候,请求过来是,是可以正常处理的。如果服务多次失败,则进入到断开状态。
  • 断开状态:当服务不能正常提供服务时,则进入到该状态。并且熔断器每隔 timeout 时间段后,会切换到半断开状态,来再次看看服务是否已经恢复好了
  • 半断开状态:在这个状态时,允许一定(少量)调用服务,如果这次服务调用都正常返回,则切换到闭合状态。如果其中有失败,则再次切换到断开状态。

熔断器请求统计

熔断器用来记录请求成功、失败情况的计数字段

type Counts struct {
    Requests             uint32
    TotalSuccesses       uint32
    TotalFailures        uint32
    ConsecutiveSuccesses uint32
    ConsecutiveFailures  uint32
}

这个字段用来判断服务是否处于正常的状态,默认如果连续失败次数达到5次,则判断服务不可用,要切换到断开状态。判断服务是否可用,也可以 赋值 ReadyToTrip 字段,来自定义判断服务是否已经不正常了。

熔断器其他字段

  • Name 熔断器名字
  • MaxRequests 这个字段限制当熔断器处于半断开状态时,允许多少各请求尝试访问服务
  • interval 这个字段标示,当处于闭合状态时,每隔多久清零请求统计,如果为0,则不清零。
  • readyToTrip 当调用服务失败时,调用这个函数,会将当前的请求统计情况对象作为参数传入
  • onStateChange 当熔断器状态改变时调用这个函数
  • generation 状态每变化一次,该值增加一次如果这是了interval,则在闭合状态时,每隔interval时,也增加一次
  • expiry 设置超时时间点,比如当前是断开状态,何时到半断开状态

熔断器使用

说了这么多,到底怎么用呢?可以看下自带的示例

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"

    "github.com/sony/gobreaker"
)

var cb *gobreaker.CircuitBreaker

func init() {
    var st gobreaker.Settings
    st.Name = "HTTP GET"
    st.ReadyToTrip = func(counts gobreaker.Counts) bool {
        failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
        return counts.Requests >= 3 && failureRatio >= 0.6
    }

    cb = gobreaker.NewCircuitBreaker(st)
}

// Get wraps http.Get in CircuitBreaker.
func Get(url string) ([]byte, error) {
    body, err := cb.Execute(func() (interface{}, error) {
        resp, err := http.Get(url)
        if err != nil {
            return nil, err
        }

        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            return nil, err
        }

        return body, nil
    })
    if err != nil {
        return nil, err
    }

    return body.([]byte), nil
}

func main() {
    body, err := Get("http://www.google.com/robots.txt")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("%s", string(body))
}

先初始化一个熔断器,设置自定义的判断服务是否正常的函数,然后通过熔断器去调用服务,熔断器内部会记录每次调用是否成功,根据实际情况会转换自身的状态。

你可能感兴趣的:(熔断器模式)