diff --git a/gomore/06_circuit_breaker/breaker_options.go b/gomore/06_circuit_breaker/breaker_options.go new file mode 100644 index 0000000..9ecf554 --- /dev/null +++ b/gomore/06_circuit_breaker/breaker_options.go @@ -0,0 +1,42 @@ +package circuit + +import "time" + +//Options for breaker +type Options struct { + Name string + Expiry time.Time + Interval time.Duration + Timeout time.Duration + MaxRequests uint32 + ReadyToTrip StateCheckerHandler + OnStateChanged StateChangedEventHandler +} + +//SetName of breaker +func SetName(name string) Option { + return func(opts *Options) { + opts.Name = name + } +} + +//SetExpiry of breaker +func SetExpiry(expiry time.Time) Option { + return func(opts *Options) { + opts.Expiry = expiry + } +} + +//SetStateChangedHandle set handle of ChangedHandle +func SetStateChangedHandle(handler StateChangedEventHandler) Option { + return func(opts *Options) { + opts.OnStateChanged = handler + } +} + +//SetReadyToTrip check traffic state ,to see if request can go +func SetReadyToTrip(readyToGo StateCheckerHandler) Option { + return func(opts *Options) { + opts.ReadyToTrip = readyToGo + } +} diff --git a/gomore/06_circuit_breaker/circuit_breaker.go b/gomore/06_circuit_breaker/circuit_breaker.go index 1b16366..182425f 100644 --- a/gomore/06_circuit_breaker/circuit_breaker.go +++ b/gomore/06_circuit_breaker/circuit_breaker.go @@ -1,11 +1,25 @@ package circuit +/* + * @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 + */ + import ( "context" "errors" + "sync" "time" ) +//////////////////////////////// +///使用HTTP请求的例子 +//每个搜索引擎时时刻刻都会遇到超大规模的请求的流量. +//这里演示一个复杂一点的例子,同时使用Option 模式 + //ErrServiceUnavailable for error var ( ErrTooManyRequests = errors.New("too many requests") @@ -13,6 +27,29 @@ var ( FailureThreshold = 10 ) +//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 { + +} + //State of current switch type State int diff --git a/gomore/06_circuit_breaker/circuit_breaker_test.go b/gomore/06_circuit_breaker/circuit_breaker_test.go index 1bba66f..e0f6daa 100644 --- a/gomore/06_circuit_breaker/circuit_breaker_test.go +++ b/gomore/06_circuit_breaker/circuit_breaker_test.go @@ -1,9 +1,51 @@ package circuit import ( + "fmt" + "io/ioutil" + "net/http" "testing" ) +var breaker *RequestBreaker + func TestBasicBreaker(t *testing.T) { + var st Options + st.Name = "HTTP GET" + st.ReadyToTrip = func(counts counters) bool { + failureRatio := float64(counts.TotalFailures) / float64(counts.Requests) + return counts.Requests >= 3 && failureRatio >= 0.6 + } + + breaker = NewRequestBreaker(st) + + body, err := Get("https://bing.com/robots.txt") + if err != nil { + t.Fatal(err) + } + + fmt.Println(string(body)) +} + +// Get wraps http.Get in CircuitBreaker. +func Get(url string) ([]byte, error) { + body, err := breaker.Execute(func() (interface{}, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, nil + }) + if err != nil { + return nil, err + } + + return body.([]byte), nil + }