go-pattern-examples/resiliency/06_circuit_breaker/circuit_func_closure.go
2020-05-22 13:21:54 +08:00

97 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Description: https://github.com/crazybber
* @Author: Edward
* @Date: 2020-05-22 12:42:34
* @Last Modified by: Edward
* @Last Modified time: 2020-05-22 12:42:34
*/
package circuit
import (
"context"
"time"
)
////////////////////////////////
//way 2 简单的函数式断路器
// 一个func实例作为一个Breaker 允许多个worker(即goroutine)同时访问
// 理论上讲也需要考虑同步问题
////////////////////////////////
//Circuit of action stream,this is actually to do something.
//Circuit hold the really action
type Circuit func(context.Context) error
//Breaker return a closure wrapper to hold request,达到指定的失败次数后电路断开
func Breaker(c Circuit, failureThreshold uint32) Circuit {
//内部计数器
cnt := counters{}
expired := time.Now()
currentState := StateClosed //默认是闭合状态
//ctx can be used hold parameters
return func(ctx context.Context) error {
if cnt.ConsecutiveFailures >= failureThreshold {
//断路器在half open状态下的控制逻辑
canRetry := func(cnt counters) bool {
//间歇时间多个线程时候会存在同步文件需要lock操作
backoffLevel := cnt.ConsecutiveFailures - failureThreshold
// Calculates when should the circuit breaker resume propagating requests
// to the service
shouldRetryAt := cnt.LastActivity().Add(time.Second << backoffLevel)
return time.Now().After(shouldRetryAt)
}
//如果仍然不能执行,直接返回失败
if !canRetry(cnt) {
// Fails fast instead of propagating requests to the circuit since
// not enough time has passed since the last failure to retry
return ErrServiceUnavailable
}
}
// 可以执行,则执行,并累计成功和失败次数
// Unless the failure threshold is exceeded the wrapped service mimics the
// old behavior and the difference in behavior is seen after consecutive failures
// do the job
//handle statue transformation for timeout
if currentState == StateOpen {
nowt := time.Now()
if expired.Before(nowt) || expired.Equal(nowt) {
currentState = StateHalfOpen //端开状态的计时器过期了,转为半开
cnt.ConsecutiveSuccesses = 0 //重置用于累计成功调用的计数器
}
}
switch currentState {
case StateOpen:
return ErrServiceUnavailable //直接失败
case StateHalfOpen:
if err := c(ctx); err != nil {
//统计状态
cnt.Count(FailureState)
currentState = StateOpen
expired = time.Now().Add(defaultTimeout) //Reset默认的超时时间
return err
}
//统计成功状态
cnt.Count(SuccessState)
//处理状态转换
if cnt.ConsecutiveSuccesses > defaultSuccessThreshold {
currentState = StateClosed
cnt.ConsecutiveFailures = 0
}
return nil
case StateClosed:
}
return nil
}
}