go-pattern-examples/resiliency/06_circuit_breaker/circuit_breaker.go

234 lines
5.8 KiB
Go
Raw Normal View History

2020-05-10 17:03:24 +03:00
/*
* @Description: https://github.com/crazybber
* @Author: Edward
* @Date: 2020-05-10 22:00:58
* @Last Modified by: Edward
2020-05-21 12:45:42 +03:00
* @Last Modified time: 2020-05-21 17:41:46
2020-05-10 17:03:24 +03:00
*/
2020-05-21 12:45:42 +03:00
package circuit
2020-05-08 10:51:33 +03:00
import (
"context"
"errors"
2020-05-21 11:02:28 +03:00
"fmt"
2020-05-10 17:03:24 +03:00
"sync"
2020-05-08 10:51:33 +03:00
"time"
)
2020-05-10 17:03:24 +03:00
////////////////////////////////
///使用HTTP请求的例子
//每个搜索引擎时时刻刻都会遇到超大规模的请求的流量.
//这里演示一个复杂一点的例子同时使用Option 模式
2020-05-08 10:51:33 +03:00
//ErrServiceUnavailable for error
var (
2020-05-08 14:34:48 +03:00
ErrTooManyRequests = errors.New("too many requests")
ErrServiceUnavailable = errors.New("service unavailable")
2020-05-21 11:02:28 +03:00
FailureThreshold = 10 //最大失败次数--->失败阈值
2020-05-08 10:51:33 +03:00
)
2020-05-21 12:45:42 +03:00
// 默认的超时时间
const defaultTimeout = time.Second * 30
2020-05-21 11:02:28 +03:00
////////////////////////////////
/// 状态计数器 用以维护断路器内部的状态
/// 无论是对象式断路器还是函数式断路器
/// 都要用到计数器
////////////////////////////////
2020-05-14 18:35:44 +03:00
2020-05-21 12:45:42 +03:00
//State 断路器本身的状态的
//State of switch int
2020-05-08 14:34:48 +03:00
type State int
2020-05-21 12:45:42 +03:00
// state for breaker
const (
StateClosed State = iota //默认的闭合状态,可以正常执行业务
StateHalfOpen
StateOpen
)
//OperationState of current 某一次操作的结果状态
type OperationState int
2020-05-08 14:34:48 +03:00
//states of CircuitBreaker
2020-05-21 12:45:42 +03:00
//states: closed --->open ---> half open --> closed
2020-05-08 10:51:33 +03:00
const (
2020-05-21 12:45:42 +03:00
UnknownState OperationState = iota
2020-05-08 10:51:33 +03:00
FailureState
SuccessState
)
2020-05-11 13:01:25 +03:00
//ICounter interface
type ICounter interface {
2020-05-21 12:45:42 +03:00
Count(OperationState)
2020-05-08 10:51:33 +03:00
LastActivity() time.Time
Reset()
2020-05-21 11:02:28 +03:00
Total() uint32
2020-05-08 10:51:33 +03:00
}
type counters struct {
2020-05-21 11:02:28 +03:00
Requests uint32 //连续的请求次数
2020-05-21 12:45:42 +03:00
lastState OperationState
2020-05-11 17:15:58 +03:00
lastActivity time.Time
counts uint32 //counts of failures
TotalFailures uint32
TotalSuccesses uint32
ConsecutiveSuccesses uint32
ConsecutiveFailures uint32
2020-05-08 10:51:33 +03:00
}
2020-05-21 11:02:28 +03:00
func (c *counters) Total() uint32 {
return c.Requests
}
2020-05-08 10:51:33 +03:00
func (c *counters) LastActivity() time.Time {
return c.lastActivity
}
func (c *counters) Reset() {
2020-05-21 11:02:28 +03:00
ct := &counters{}
ct.lastActivity = c.lastActivity
ct.lastState = c.lastState
c = ct
2020-05-08 10:51:33 +03:00
}
2020-05-15 13:31:56 +03:00
//Count the failure and success
2020-05-21 12:45:42 +03:00
func (c *counters) Count(statue OperationState) {
2020-05-11 17:15:58 +03:00
switch statue {
case FailureState:
c.ConsecutiveFailures++
case SuccessState:
c.ConsecutiveSuccesses++
}
c.Requests++
2020-05-21 11:02:28 +03:00
c.lastActivity = time.Now() //更新活动时间
2020-05-11 17:15:58 +03:00
c.lastState = statue
2020-05-21 12:45:42 +03:00
//handle status change
2020-05-21 11:02:28 +03:00
2020-05-08 10:51:33 +03:00
}
2020-05-21 11:02:28 +03:00
////////////////////////////////
//way 1 对象式断路器
////////////////////////////////
//RequestBreaker for protection
type RequestBreaker struct {
options Options
mutex sync.Mutex
2020-05-21 12:45:42 +03:00
state OperationState //断路器的当前状态
2020-05-21 11:02:28 +03:00
generation uint64
counts ICounter
}
//NewRequestBreaker return a breaker
func NewRequestBreaker(opts ...Option) *RequestBreaker {
defaultOptions := Options{
Name: "defaultBreakerName",
Expiry: time.Now().Add(time.Second * 20),
Interval: time.Second * 2, // interval to check status
Timeout: time.Second * 60, //default to 60 seconds
MaxRequests: 5,
WhenToBreak: func(counts counters) bool { return counts.ConsecutiveFailures > 2 },
OnStateChanged: func(name string, fromPre State, toCurrent State) {},
}
for _, setOption := range opts {
setOption(&defaultOptions)
}
return &RequestBreaker{
options: defaultOptions,
counts: &counters{},
generation: 0,
}
}
// Do the given requested work if the RequestBreaker accepts it.
// Do returns an error instantly if the RequestBreaker rejects the request.
// Otherwise, Execute returns the result of the request.
// If a panic occurs in the request, the RequestBreaker handles it as an error and causes the same panic again.
func (rb *RequestBreaker) Do(work func() (interface{}, error)) (interface{}, error) {
//before
fmt.Println("before do : request:", rb.counts.Total())
//do work from requested user
result, err := work()
fmt.Println("after do : request:", rb.counts.Total())
return result, err
}
////////////////////////////////
//way 2 简单的函数式断路器
////////////////////////////////
//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 {
2020-05-08 10:51:33 +03:00
2020-05-15 13:31:56 +03:00
//内部计数器
2020-05-11 17:15:58 +03:00
cnt := counters{}
2020-05-21 12:45:42 +03:00
expired := time.Now()
currentState := StateClosed //默认是闭合状态
2020-05-08 10:51:33 +03:00
2020-05-21 11:02:28 +03:00
//ctx can be used hold parameters
2020-05-08 10:51:33 +03:00
return func(ctx context.Context) error {
2020-05-11 17:15:58 +03:00
if cnt.ConsecutiveFailures >= failureThreshold {
2020-05-21 12:45:42 +03:00
//断路器在half open状态下的控制逻辑
2020-05-11 17:15:58 +03:00
canRetry := func(cnt counters) bool {
2020-05-21 11:02:28 +03:00
//间歇时间多个线程时候会存在同步文件需要lock操作
2020-05-11 17:15:58 +03:00
backoffLevel := cnt.ConsecutiveFailures - failureThreshold
2020-05-08 10:51:33 +03:00
// Calculates when should the circuit breaker resume propagating requests
// to the service
2020-05-21 12:45:42 +03:00
shouldRetryAt := cnt.LastActivity().Add(time.Second << backoffLevel)
2020-05-08 10:51:33 +03:00
return time.Now().After(shouldRetryAt)
}
2020-05-21 11:02:28 +03:00
//如果仍然不能执行,直接返回失败
2020-05-08 10:51:33 +03:00
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
}
}
2020-05-21 12:45:42 +03:00
// 可以执行,则执行,并累计成功和失败次数
2020-05-08 10:51:33 +03:00
// Unless the failure threshold is exceeded the wrapped service mimics the
// old behavior and the difference in behavior is seen after consecutive failures
2020-05-21 11:02:28 +03:00
// do the job
2020-05-21 12:45:42 +03:00
switch currentState {
case StateOpen:
if time.Now().Before(expired) {
currentState = StateHalfOpen //转为半开
}
return ErrServiceUnavailable
case StateClosed:
case StateHalfOpen:
}
2020-05-08 10:51:33 +03:00
if err := c(ctx); err != nil {
2020-05-21 11:02:28 +03:00
//统计状态
2020-05-08 10:51:33 +03:00
cnt.Count(FailureState)
2020-05-21 12:45:42 +03:00
2020-05-08 10:51:33 +03:00
return err
}
2020-05-21 11:02:28 +03:00
//统计成功状态
2020-05-08 10:51:33 +03:00
cnt.Count(SuccessState)
return nil
}
}