feat: optimize circuit breaker algorithm (#3897)
This commit is contained in:
@@ -53,16 +53,19 @@ type (
|
|||||||
// DoWithFallback runs the fallback if the Breaker rejects the request.
|
// DoWithFallback runs the fallback if the Breaker rejects the request.
|
||||||
// If a panic occurs in the request, the Breaker handles it as an error
|
// If a panic occurs in the request, the Breaker handles it as an error
|
||||||
// and causes the same panic again.
|
// and causes the same panic again.
|
||||||
DoWithFallback(req func() error, fallback func(err error) error) error
|
DoWithFallback(req func() error, fallback Fallback) error
|
||||||
|
|
||||||
// DoWithFallbackAcceptable runs the given request if the Breaker accepts it.
|
// DoWithFallbackAcceptable runs the given request if the Breaker accepts it.
|
||||||
// DoWithFallbackAcceptable runs the fallback if the Breaker rejects the request.
|
// DoWithFallbackAcceptable runs the fallback if the Breaker rejects the request.
|
||||||
// If a panic occurs in the request, the Breaker handles it as an error
|
// If a panic occurs in the request, the Breaker handles it as an error
|
||||||
// and causes the same panic again.
|
// and causes the same panic again.
|
||||||
// acceptable checks if it's a successful call, even if the error is not nil.
|
// acceptable checks if it's a successful call, even if the error is not nil.
|
||||||
DoWithFallbackAcceptable(req func() error, fallback func(err error) error, acceptable Acceptable) error
|
DoWithFallbackAcceptable(req func() error, fallback Fallback, acceptable Acceptable) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback is the func to be called if the request is rejected.
|
||||||
|
Fallback func(err error) error
|
||||||
|
|
||||||
// Option defines the method to customize a Breaker.
|
// Option defines the method to customize a Breaker.
|
||||||
Option func(breaker *circuitBreaker)
|
Option func(breaker *circuitBreaker)
|
||||||
|
|
||||||
@@ -86,12 +89,12 @@ type (
|
|||||||
|
|
||||||
internalThrottle interface {
|
internalThrottle interface {
|
||||||
allow() (internalPromise, error)
|
allow() (internalPromise, error)
|
||||||
doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error
|
doReq(req func() error, fallback Fallback, acceptable Acceptable) error
|
||||||
}
|
}
|
||||||
|
|
||||||
throttle interface {
|
throttle interface {
|
||||||
allow() (Promise, error)
|
allow() (Promise, error)
|
||||||
doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error
|
doReq(req func() error, fallback Fallback, acceptable Acceptable) error
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -122,11 +125,11 @@ func (cb *circuitBreaker) DoWithAcceptable(req func() error, acceptable Acceptab
|
|||||||
return cb.throttle.doReq(req, nil, acceptable)
|
return cb.throttle.doReq(req, nil, acceptable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cb *circuitBreaker) DoWithFallback(req func() error, fallback func(err error) error) error {
|
func (cb *circuitBreaker) DoWithFallback(req func() error, fallback Fallback) error {
|
||||||
return cb.throttle.doReq(req, fallback, defaultAcceptable)
|
return cb.throttle.doReq(req, fallback, defaultAcceptable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cb *circuitBreaker) DoWithFallbackAcceptable(req func() error, fallback func(err error) error,
|
func (cb *circuitBreaker) DoWithFallbackAcceptable(req func() error, fallback Fallback,
|
||||||
acceptable Acceptable) error {
|
acceptable Acceptable) error {
|
||||||
return cb.throttle.doReq(req, fallback, acceptable)
|
return cb.throttle.doReq(req, fallback, acceptable)
|
||||||
}
|
}
|
||||||
@@ -168,7 +171,7 @@ func (lt loggedThrottle) allow() (Promise, error) {
|
|||||||
}, lt.logError(err)
|
}, lt.logError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lt loggedThrottle) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error {
|
func (lt loggedThrottle) doReq(req func() error, fallback Fallback, acceptable Acceptable) error {
|
||||||
return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool {
|
return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool {
|
||||||
accept := acceptable(err)
|
accept := acceptable(err)
|
||||||
if !accept && err != nil {
|
if !accept && err != nil {
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ func DoWithAcceptable(name string, req func() error, acceptable Acceptable) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DoWithFallback calls Breaker.DoWithFallback on the Breaker with given name.
|
// DoWithFallback calls Breaker.DoWithFallback on the Breaker with given name.
|
||||||
func DoWithFallback(name string, req func() error, fallback func(err error) error) error {
|
func DoWithFallback(name string, req func() error, fallback Fallback) error {
|
||||||
return do(name, func(b Breaker) error {
|
return do(name, func(b Breaker) error {
|
||||||
return b.DoWithFallback(req, fallback)
|
return b.DoWithFallback(req, fallback)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoWithFallbackAcceptable calls Breaker.DoWithFallbackAcceptable on the Breaker with given name.
|
// DoWithFallbackAcceptable calls Breaker.DoWithFallbackAcceptable on the Breaker with given name.
|
||||||
func DoWithFallbackAcceptable(name string, req func() error, fallback func(err error) error,
|
func DoWithFallbackAcceptable(name string, req func() error, fallback Fallback,
|
||||||
acceptable Acceptable) error {
|
acceptable Acceptable) error {
|
||||||
return do(name, func(b Breaker) error {
|
return do(name, func(b Breaker) error {
|
||||||
return b.DoWithFallbackAcceptable(req, fallback, acceptable)
|
return b.DoWithFallbackAcceptable(req, fallback, acceptable)
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ func (b *googleBreaker) allow() (internalPromise, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error {
|
func (b *googleBreaker) doReq(req func() error, fallback Fallback, acceptable Acceptable) error {
|
||||||
if err := b.accept(); err != nil {
|
if err := b.accept(); err != nil {
|
||||||
|
b.markFailure()
|
||||||
if fallback != nil {
|
if fallback != nil {
|
||||||
return fallback(err)
|
return fallback(err)
|
||||||
}
|
}
|
||||||
@@ -69,18 +70,19 @@ func (b *googleBreaker) doReq(req func() error, fallback func(err error) error,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var success bool
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
// if req() panic, success is false, mark as failure
|
||||||
|
if success {
|
||||||
|
b.markSuccess()
|
||||||
|
} else {
|
||||||
b.markFailure()
|
b.markFailure()
|
||||||
panic(e)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := req()
|
err := req()
|
||||||
if acceptable(err) {
|
if acceptable(err) {
|
||||||
b.markSuccess()
|
success = true
|
||||||
} else {
|
|
||||||
b.markFailure()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ func BenchmarkGoogleBreakerAllow(b *testing.B) {
|
|||||||
breaker := getGoogleBreaker()
|
breaker := getGoogleBreaker()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i <= b.N; i++ {
|
for i := 0; i <= b.N; i++ {
|
||||||
breaker.accept()
|
_ = breaker.accept()
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
breaker.markSuccess()
|
breaker.markSuccess()
|
||||||
} else {
|
} else {
|
||||||
@@ -215,6 +215,16 @@ func BenchmarkGoogleBreakerAllow(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkGoogleBreakerDoReq(b *testing.B) {
|
||||||
|
breaker := getGoogleBreaker()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i <= b.N; i++ {
|
||||||
|
_ = breaker.doReq(func() error {
|
||||||
|
return nil
|
||||||
|
}, nil, defaultAcceptable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func markSuccess(b *googleBreaker, count int) {
|
func markSuccess(b *googleBreaker, count int) {
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
p, err := b.allow()
|
p, err := b.allow()
|
||||||
|
|||||||
@@ -24,12 +24,11 @@ func (b nopBreaker) DoWithAcceptable(req func() error, _ Acceptable) error {
|
|||||||
return req()
|
return req()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b nopBreaker) DoWithFallback(req func() error, _ func(err error) error) error {
|
func (b nopBreaker) DoWithFallback(req func() error, _ Fallback) error {
|
||||||
return req()
|
return req()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b nopBreaker) DoWithFallbackAcceptable(req func() error, _ func(err error) error,
|
func (b nopBreaker) DoWithFallbackAcceptable(req func() error, _ Fallback, _ Acceptable) error {
|
||||||
_ Acceptable) error {
|
|
||||||
return req()
|
return req()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -603,11 +603,11 @@ func (d *dropBreaker) DoWithAcceptable(_ func() error, _ breaker.Acceptable) err
|
|||||||
return errDummy
|
return errDummy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dropBreaker) DoWithFallback(_ func() error, _ func(err error) error) error {
|
func (d *dropBreaker) DoWithFallback(_ func() error, _ breaker.Fallback) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dropBreaker) DoWithFallbackAcceptable(_ func() error, _ func(err error) error,
|
func (d *dropBreaker) DoWithFallbackAcceptable(_ func() error, _ breaker.Fallback,
|
||||||
_ breaker.Acceptable) error {
|
_ breaker.Acceptable) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user