Circuit Breaker pattern
In the previous post, i have implemented simple circuit-breaker pattern with Java, and today i will do it with Go
Talk about advanced of Go
+ Golang has great concurrency, the memory required for creating thread is very light.
+ Code is simple and very clean, easy to lean.
+ Appropriate and effective for service application.
+ ……
Focus on main issue of this post, i will talk about step by step to implement breaker pattern
+ Create package and file breaker.go into the folder
+ Define state service and state breaker
type State inttype State int
type StateService int
const (
STATE_CLOSE State = 1
STATE_HALF_OPEN State = 2
STATE_OPEN State = 3
SERVICE_AVAILABLE StateService = 1
SERVICE_UNAVAILABLE StateService = 0
)
+ Define Breaker, BreakerEvent, OptionsConfig, CounterResult
type CounterResult struct {
TotalRequests uint32
TotalSucceses uint32
TotalFailures uint32
TotalRejects uint32
}
type Breaker struct { options OptionsConfig counter CounterResult events []chan BreakerEvent state State consecFailures uint32 }
type BreakerEvent struct { Code StateService Message string }
type OptionsConfig struct {
Attempts uint32
TimeoutState time.Duration
LimitFailure uint32
MaxRequests uint32
NameService string
}
+ Create new instance breaker
func NewBreaker(options *OptionsConfig) *Breaker {
if options == nil {
options = &OptionsConfig{}
}
if options.Attempts == 0 {
options.Attempts = 3
}
if options.TimeoutState == 0 {
options.TimeoutState = 4 * time.Second
}
if options.LimitFailure == 0 {
options.LimitFailure = 5
}
if options.MaxRequests == 0 {
options.MaxRequests = 10000000
}
if options.NameService == "" {
options.NameService = strconv.Itoa(int(time.Now().UnixNano()))
}
return &Breaker{options: *options, counter: CounterResult{}, state: STATE_CLOSE, consecFailures: 0}
}
+ Allow other services subscribe event
// other services subcriber
func (cb *Breaker) Subcriber() <-chan BreakerEvent {
evenReader := make(chan BreakerEvent)
outputChannel := make(chan BreakerEvent, 100)
go func() {
for event := range evenReader {
select {
case outputChannel <- event:
default:<-outputChannel
outputChannel <- event
}
}
}()
cb.events = append(cb.events, evenReader)
return outputChannel
}
+ Create execute handle
// execute handle
func (cb *Breaker) Execute(handle func() ResponseCommand, timeout time.Duration) ResponseCommand {
// add request
atomic.AddUint32(&cb.counter.TotalRequests, 1)
response := ResponseCommand{}
// Reject all invoke when current state is OPEN
if cb.IsOpen() {
cb.Reject()
response.Error = cb.ErrorServiceUnavailable()
response.Data = nil
return response
}
// run handle immediate and execute time is unlimited
if timeout == 0 {
response = handle()
} else {
// run handle with time execute is limited
c := make(chan ResponseCommand, 1)
go func() {
c <- handle()
}()
select {
case r := <-c:
response = r
close(c)
case <-time.NewTimer(timeout).C:
response.Error = cb.ErrorTimeoutExecute()
response.Data = nil
}
}
// check for any errors
if response.Error != nil {
cb.Failure()
} else {
cb.Success()
}
return response
}
You can reference full source and demo at : circuit-breaker-go