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

138 lines
2.8 KiB
Go
Raw Normal View History

2020-05-08 10:51:33 +03:00
package circuit
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
* @Last Modified time: 2020-05-10 22:02:18
*/
2020-05-08 10:51:33 +03:00
import (
"context"
"errors"
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-08 10:51:33 +03:00
FailureThreshold = 10
)
2020-05-10 17:03:24 +03:00
//StateCheckerHandler check state
type StateCheckerHandler func(counts counters) bool
//StateChangedEventHandler set event handle
type StateChangedEventHandler func(name string, from State, to State)
//Option set Options
type Option func(opts *Options)
//RequestBreaker for protection
type RequestBreaker struct {
options Options
mutex sync.Mutex
state State
generation uint64
counts Counter
}
//NewRequestBreaker return a breaker
func NewRequestBreaker() *RequestBreaker {
}
2020-05-08 10:51:33 +03:00
//State of current switch
2020-05-08 14:34:48 +03:00
type State int
//states of CircuitBreaker
2020-05-08 10:51:33 +03:00
const (
2020-05-08 14:34:48 +03:00
UnknownState State = iota
2020-05-08 10:51:33 +03:00
FailureState
SuccessState
)
//Circuit of action stream
type Circuit func(context.Context) error
//Counter interface
type Counter interface {
2020-05-08 14:34:48 +03:00
Count(State)
2020-05-08 10:51:33 +03:00
ConsecutiveFailures() uint32
LastActivity() time.Time
Reset()
}
type counters struct {
2020-05-08 14:34:48 +03:00
state State
2020-05-08 10:51:33 +03:00
lastActivity time.Time
counts uint32 //counts of failures
}
2020-05-08 14:34:48 +03:00
func (c *counters) Count(state State) {
2020-05-08 10:51:33 +03:00
}
func (c *counters) ConsecutiveFailures() uint32 {
return 0
}
func (c *counters) LastActivity() time.Time {
return c.lastActivity
}
func (c *counters) Reset() {
}
//NewCounter New Counter for Circuit Breaker
func NewCounter() Counter {
return &counters{}
}
//Breaker of circuit
func Breaker(c Circuit, failureThreshold uint32) Circuit {
cnt := NewCounter()
return func(ctx context.Context) error {
if cnt.ConsecutiveFailures() >= failureThreshold {
canRetry := func(cnt Counter) bool {
backoffLevel := cnt.ConsecutiveFailures() - failureThreshold
// Calculates when should the circuit breaker resume propagating requests
// to the service
shouldRetryAt := cnt.LastActivity().Add(time.Second * 2 << 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
if err := c(ctx); err != nil {
cnt.Count(FailureState)
return err
}
cnt.Count(SuccessState)
return nil
}
}