用golang实现一个熔断器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jeffrey11223/article/details/85541923

 
首先贴出仓库地址https://github.com/JeffreyDing11223/goBreaker

这个熔断器实现简单,使用方便,同时大家也可以fork后进行自己需要的改造,当然,也欢迎大家提pr,一起优化goBreaker

下面介绍下goBreaker:

goBreaker状态机

在这里插入图片描述

状态转换逻辑

  • 初始为closed状态,一旦遇到请求失败时,会触发熔断检测(见下方的 ShouldTrip),熔断检测来决定是否将状态从closed转为open。
  • 当熔断器为open状态时,会熔断所有当前服务要发出去的请求,直到冷却时间(见下方的CoolingTimeout)结束,会从open转变为half-open状态。
  • 当熔断器为half-open状态时,以检测时间(见下方的 DetectTimeout)为周期去发送请求。请求成功则计数器加1,当计数器达到一定阈值时则转为closed状态;请求失败则转为open状态。

熔断器内部数据结构

type Breaker struct {
	Container // contains all success, error and timeout
	sync.RWMutex

	state           State
	openTime        time.Time // the time when the breaker become OPEN
	lastRetryTime   time.Time // last retry time when in HALFOPEN state
	halfopenSuccess int       // consecutive successes when HALFOPEN

	options Options

	now func() time.Time
}

Breaker是暴露在最外层的struct,由以下属性组成:

  • Container:是一个interface,被 window 实现,负责熔断器请求失败,成功的相关计算和统计
  • RW锁:在http-gateway中,针对每个cmd有一个熔断器,每个 cmd 同时会有多个goroutine并发请求,需要RW锁来保持熔断器中计数器,状态等等的同步
  • state:熔断器三种状态,closed,open 和 half-open
  • openTime:当熔断器变为 open 状态时,记录下的时间
  • lastRetryTime:在 half-open 状态时,会有个检测周期,即每隔这个周期之后,熔断器会放请求出去,同时更新这个 lastRetryTime。
  • halfopenSuccess:在 half-open状态时,当请求成功时,halfopenSuccess 会+1,当 halfopenSuccess 等于一个阈值时(默认为2),则变为 closed 状态
  • options:Breaker 的配置项,包括桶持有数量持有时间,冷却时间,检测周期,熔断检测回调和状态变化回调等等
// Options for Breaker
type Options struct {
	// parameters for container
	BucketTime time.Duration // the time each bucket holds
	BucketNums int           // the number of buckets the breaker have

	// parameters for breaker
	BreakerRate        float64
	BreakerMinQPS      int           // when instance > 1, if qps is over this value, the breaker trip will work
	BreakerMinSamples  int           // for RateTrip callback
	CoolingTimeout     time.Duration // fixed when create
	DetectTimeout      time.Duration // fixed when create
	HalfOpenSuccess    int
	ShouldTrip         TripFunc // trip callback, default is RateTrip func
	StateChangeHandler StateChangeHandler

	now func() time.Time
}

options是Breaker的配置项,有以下属性组成:

  • BucketTime:桶的在线时间
  • BucketNums:window下持有桶的数量
  • BrekaerRate:熔断检测回调RateTrip的阈值
  • BreakerMinQPS:当实例数量大于1时,并且开启了动态策略时,用于计算BreakerMinSamples
  • BreakerMinSamples:最小采样数,配合RateTrip熔断检测回调使用
  • CoolingTimeout:保持 open 状态直到冷却时间结束,会从 open 转变为 half-open 状态,默认为5秒
  • DetectTimeout:half-open 状态时,每隔这个周期之后,熔断器会放请求出去
  • HalfOpenSuccess:half-open状态变为closed状态的判断指标
  • ShouldTrip:熔断检测回调,为nil则代表不启用熔断功能
  • StateChangeHandler:状态变化回调
type window struct {
	sync.Mutex
	oldest  int       // oldest bucket index
	latest  int       // latest bucket index
	buckets []*bucket // buckets this window has

	bucketTime time.Duration // time each bucket holds
	bucketNums int           // the largest number of buckets of window could have
	expireTime time.Duration // expire time of this window, equals to window size
	inWindow   int           // the number of buckets in the window currently

	conseErr int64 //consecutive errors
}

window负责熔断器请求失败,成功的相关计算和统计,有以下属性组成:

  • 互斥锁:保证内部数据同步
  • oldest:最老的桶,由 latest 桶变化而来,用于窗口下所有请求结果的存储
  • latest:最新的桶,每次统计请求结果时,用最新的桶来存储
  • buckets:所有桶
  • bucketTime:latest 桶的在线时间,一旦 latest 桶下线,则变为 oldest 桶
  • bucketNums:窗口最大持有桶的数量
  • expireTime:oldest 桶的过期时间,一旦 oldest 桶过期,则从 window 中“移去”,expireTime = bucketTime*bucketNums
  • inWindow:窗口当下持有桶的数量
  • conseErr:连续错误数量,每次请求结果为成功时便清零

熔断检测回调:

  1. ThresholdTripFunc:当失败和超时的总数超过阈值,则熔断
  2. ConsecutiveTripFunc:当连续错误总数(conseErr)超过阈值,则熔断
  3. RateTripFunc:当窗口内请求总数大于最小采样数且错误率(失败+超时数量/请求总数)大于一定值时,则熔断

api

  • InitCircuitBreakers方法作为初始化熔断器使用,这里用cmd来区分各个breaker
  • BreakerWhitelist 可以配置熔断白名单,在白名单中的cmd不参与熔断
  • IsTriggerBreaker 判断当前cmd的熔断器的状态,并告诉上层
  • api比较简单,可以直接阅读源码来决定如何使用,仓库地址见文首

猜你喜欢

转载自blog.csdn.net/jeffrey11223/article/details/85541923