You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
3.6 KiB
176 lines
3.6 KiB
package breaker
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
xtime "github.com/bilibili/kratos/pkg/time"
|
|
)
|
|
|
|
// Config broker config.
|
|
type Config struct {
|
|
SwitchOff bool // breaker switch,default off.
|
|
|
|
// Hystrix
|
|
Ratio float32
|
|
Sleep xtime.Duration
|
|
|
|
// Google
|
|
K float64
|
|
|
|
Window xtime.Duration
|
|
Bucket int
|
|
Request int64
|
|
}
|
|
|
|
func (conf *Config) fix() {
|
|
if conf.K == 0 {
|
|
conf.K = 1.5
|
|
}
|
|
if conf.Request == 0 {
|
|
conf.Request = 100
|
|
}
|
|
if conf.Ratio == 0 {
|
|
conf.Ratio = 0.5
|
|
}
|
|
if conf.Sleep == 0 {
|
|
conf.Sleep = xtime.Duration(500 * time.Millisecond)
|
|
}
|
|
if conf.Bucket == 0 {
|
|
conf.Bucket = 10
|
|
}
|
|
if conf.Window == 0 {
|
|
conf.Window = xtime.Duration(3 * time.Second)
|
|
}
|
|
}
|
|
|
|
// Breaker is a CircuitBreaker pattern.
|
|
// FIXME on int32 atomic.LoadInt32(&b.on) == _switchOn
|
|
type Breaker interface {
|
|
Allow() error
|
|
MarkSuccess()
|
|
MarkFailed()
|
|
}
|
|
|
|
// Group represents a class of CircuitBreaker and forms a namespace in which
|
|
// units of CircuitBreaker.
|
|
type Group struct {
|
|
mu sync.RWMutex
|
|
brks map[string]Breaker
|
|
conf *Config
|
|
}
|
|
|
|
const (
|
|
// StateOpen when circuit breaker open, request not allowed, after sleep
|
|
// some duration, allow one single request for testing the health, if ok
|
|
// then state reset to closed, if not continue the step.
|
|
StateOpen int32 = iota
|
|
// StateClosed when circuit breaker closed, request allowed, the breaker
|
|
// calc the succeed ratio, if request num greater request setting and
|
|
// ratio lower than the setting ratio, then reset state to open.
|
|
StateClosed
|
|
// StateHalfopen when circuit breaker open, after slepp some duration, allow
|
|
// one request, but not state closed.
|
|
StateHalfopen
|
|
|
|
//_switchOn int32 = iota
|
|
// _switchOff
|
|
)
|
|
|
|
var (
|
|
_mu sync.RWMutex
|
|
_conf = &Config{
|
|
Window: xtime.Duration(3 * time.Second),
|
|
Bucket: 10,
|
|
Request: 100,
|
|
|
|
Sleep: xtime.Duration(500 * time.Millisecond),
|
|
Ratio: 0.5,
|
|
// Percentage of failures must be lower than 33.33%
|
|
K: 1.5,
|
|
|
|
// Pattern: "",
|
|
}
|
|
_group = NewGroup(_conf)
|
|
)
|
|
|
|
// Init init global breaker config, also can reload config after first time call.
|
|
func Init(conf *Config) {
|
|
if conf == nil {
|
|
return
|
|
}
|
|
_mu.Lock()
|
|
_conf = conf
|
|
_mu.Unlock()
|
|
}
|
|
|
|
// Go runs your function while tracking the breaker state of default group.
|
|
func Go(name string, run, fallback func() error) error {
|
|
breaker := _group.Get(name)
|
|
if err := breaker.Allow(); err != nil {
|
|
return fallback()
|
|
}
|
|
return run()
|
|
}
|
|
|
|
// newBreaker new a breaker.
|
|
func newBreaker(c *Config) (b Breaker) {
|
|
// factory
|
|
return newSRE(c)
|
|
}
|
|
|
|
// NewGroup new a breaker group container, if conf nil use default conf.
|
|
func NewGroup(conf *Config) *Group {
|
|
if conf == nil {
|
|
_mu.RLock()
|
|
conf = _conf
|
|
_mu.RUnlock()
|
|
} else {
|
|
conf.fix()
|
|
}
|
|
return &Group{
|
|
conf: conf,
|
|
brks: make(map[string]Breaker),
|
|
}
|
|
}
|
|
|
|
// Get get a breaker by a specified key, if breaker not exists then make a new one.
|
|
func (g *Group) Get(key string) Breaker {
|
|
g.mu.RLock()
|
|
brk, ok := g.brks[key]
|
|
conf := g.conf
|
|
g.mu.RUnlock()
|
|
if ok {
|
|
return brk
|
|
}
|
|
// NOTE here may new multi breaker for rarely case, let gc drop it.
|
|
brk = newBreaker(conf)
|
|
g.mu.Lock()
|
|
if _, ok = g.brks[key]; !ok {
|
|
g.brks[key] = brk
|
|
}
|
|
g.mu.Unlock()
|
|
return brk
|
|
}
|
|
|
|
// Reload reload the group by specified config, this may let all inner breaker
|
|
// reset to a new one.
|
|
func (g *Group) Reload(conf *Config) {
|
|
if conf == nil {
|
|
return
|
|
}
|
|
conf.fix()
|
|
g.mu.Lock()
|
|
g.conf = conf
|
|
g.brks = make(map[string]Breaker, len(g.brks))
|
|
g.mu.Unlock()
|
|
}
|
|
|
|
// Go runs your function while tracking the breaker state of group.
|
|
func (g *Group) Go(name string, run, fallback func() error) error {
|
|
breaker := g.Get(name)
|
|
if err := breaker.Allow(); err != nil {
|
|
return fallback()
|
|
}
|
|
return run()
|
|
}
|
|
|