-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
264 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# 说明 | ||
|
||
https://github.com/go-kit/kit | ||
https://github.com/dreamsxin/go-kit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package circuitbreaker | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/sony/gobreaker" | ||
|
||
"github.com/dreamsxin/go-kit/endpoint" | ||
) | ||
|
||
// Gobreaker returns an endpoint.Middleware that implements the circuit | ||
// breaker pattern using the sony/gobreaker package. Only errors returned by | ||
// the wrapped endpoint count against the circuit breaker's error count. | ||
// | ||
// See http://godoc.org/github.com/sony/gobreaker for more information. | ||
func Gobreaker(cb *gobreaker.CircuitBreaker) endpoint.Middleware { | ||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||
return func(ctx context.Context, request interface{}) (interface{}, error) { | ||
return cb.Execute(func() (interface{}, error) { return next(ctx, request) }) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package circuitbreaker_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/sony/gobreaker" | ||
|
||
"github.com/dreamsxin/go-kit/endpoint/circuitbreaker" | ||
) | ||
|
||
func TestGobreaker(t *testing.T) { | ||
var ( | ||
breaker = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{})) | ||
primeWith = 100 | ||
shouldPass = func(n int) bool { return n <= 5 } // https://github.com/sony/gobreaker/blob/bfa846d/gobreaker.go#L76 | ||
circuitOpenError = "circuit breaker is open" | ||
) | ||
testFailingEndpoint(t, breaker, primeWith, shouldPass, 0, circuitOpenError) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package circuitbreaker | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"github.com/streadway/handy/breaker" | ||
|
||
"github.com/dreamsxin/go-kit/endpoint" | ||
) | ||
|
||
// HandyBreaker returns an endpoint.Middleware that implements the circuit | ||
// breaker pattern using the streadway/handy/breaker package. Only errors | ||
// returned by the wrapped endpoint count against the circuit breaker's error | ||
// count. | ||
// | ||
// See http://godoc.org/github.com/streadway/handy/breaker for more | ||
// information. | ||
func HandyBreaker(cb breaker.Breaker) endpoint.Middleware { | ||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||
return func(ctx context.Context, request interface{}) (response interface{}, err error) { | ||
if !cb.Allow() { | ||
return nil, breaker.ErrCircuitOpen | ||
} | ||
|
||
defer func(begin time.Time) { | ||
if err == nil { | ||
cb.Success(time.Since(begin)) | ||
} else { | ||
cb.Failure(time.Since(begin)) | ||
} | ||
}(time.Now()) | ||
|
||
response, err = next(ctx, request) | ||
return | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package circuitbreaker_test | ||
|
||
import ( | ||
"testing" | ||
|
||
handybreaker "github.com/streadway/handy/breaker" | ||
|
||
"github.com/dreamsxin/go-kit/endpoint/circuitbreaker" | ||
) | ||
|
||
func TestHandyBreaker(t *testing.T) { | ||
var ( | ||
failureRatio = 0.05 | ||
breaker = circuitbreaker.HandyBreaker(handybreaker.NewBreaker(failureRatio)) | ||
primeWith = handybreaker.DefaultMinObservations * 10 | ||
shouldPass = func(n int) bool { return (float64(n) / float64(primeWith+n)) <= failureRatio } | ||
openCircuitError = handybreaker.ErrCircuitOpen.Error() | ||
) | ||
testFailingEndpoint(t, breaker, primeWith, shouldPass, 0, openCircuitError) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package circuitbreaker | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/afex/hystrix-go/hystrix" | ||
|
||
"github.com/dreamsxin/go-kit/endpoint" | ||
) | ||
|
||
// Hystrix returns an endpoint.Middleware that implements the circuit | ||
// breaker pattern using the afex/hystrix-go package. | ||
// | ||
// When using this circuit breaker, please configure your commands separately. | ||
// | ||
// See https://godoc.org/github.com/afex/hystrix-go/hystrix for more | ||
// information. | ||
func Hystrix(commandName string) endpoint.Middleware { | ||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||
return func(ctx context.Context, request interface{}) (response interface{}, err error) { | ||
var resp interface{} | ||
if err := hystrix.Do(commandName, func() (err error) { | ||
resp, err = next(ctx, request) | ||
return err | ||
}, nil); err != nil { | ||
return nil, err | ||
} | ||
return resp, nil | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package circuitbreaker_test | ||
|
||
import ( | ||
"io/ioutil" | ||
stdlog "log" | ||
"testing" | ||
"time" | ||
|
||
"github.com/afex/hystrix-go/hystrix" | ||
|
||
"github.com/dreamsxin/go-kit/endpoint/circuitbreaker" | ||
) | ||
|
||
func TestHystrix(t *testing.T) { | ||
stdlog.SetOutput(ioutil.Discard) | ||
|
||
const ( | ||
commandName = "my-endpoint" | ||
errorPercent = 5 | ||
maxConcurrent = 1000 | ||
) | ||
hystrix.ConfigureCommand(commandName, hystrix.CommandConfig{ | ||
ErrorPercentThreshold: errorPercent, | ||
MaxConcurrentRequests: maxConcurrent, | ||
}) | ||
|
||
var ( | ||
breaker = circuitbreaker.Hystrix(commandName) | ||
primeWith = hystrix.DefaultVolumeThreshold * 2 | ||
shouldPass = func(n int) bool { return (float64(n) / float64(primeWith+n)) <= (float64(errorPercent-1) / 100.0) } | ||
openCircuitError = hystrix.ErrCircuitOpen.Error() | ||
) | ||
|
||
// hystrix-go uses buffered channels to receive reports on request success/failure, | ||
// and so is basically impossible to test deterministically. We have to make sure | ||
// the report buffer is emptied, by injecting a sleep between each invocation. | ||
requestDelay := 5 * time.Millisecond | ||
|
||
testFailingEndpoint(t, breaker, primeWith, shouldPass, requestDelay, openCircuitError) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package circuitbreaker_test | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"path/filepath" | ||
"runtime" | ||
"testing" | ||
"time" | ||
|
||
"github.com/dreamsxin/go-kit/endpoint" | ||
) | ||
|
||
func testFailingEndpoint( | ||
t *testing.T, | ||
breaker endpoint.Middleware, | ||
primeWith int, | ||
shouldPass func(int) bool, | ||
requestDelay time.Duration, | ||
openCircuitError string, | ||
) { | ||
_, file, line, _ := runtime.Caller(1) | ||
caller := fmt.Sprintf("%s:%d", filepath.Base(file), line) | ||
|
||
// Create a mock endpoint and wrap it with the breaker. | ||
m := mock{} | ||
var e endpoint.Endpoint | ||
e = m.endpoint | ||
e = breaker(e) | ||
|
||
// Prime the endpoint with successful requests. | ||
for i := 0; i < primeWith; i++ { | ||
if _, err := e(context.Background(), struct{}{}); err != nil { | ||
t.Fatalf("%s: during priming, got error: %v", caller, err) | ||
} | ||
time.Sleep(requestDelay) | ||
} | ||
|
||
// Switch the endpoint to start throwing errors. | ||
m.err = errors.New("tragedy+disaster") | ||
m.through = 0 | ||
|
||
// The first several should be allowed through and yield our error. | ||
for i := 0; shouldPass(i); i++ { | ||
if _, err := e(context.Background(), struct{}{}); err != m.err { | ||
t.Fatalf("%s: want %v, have %v", caller, m.err, err) | ||
} | ||
time.Sleep(requestDelay) | ||
} | ||
through := m.through | ||
|
||
// But the rest should be blocked by an open circuit. | ||
for i := 0; i < 10; i++ { | ||
if _, err := e(context.Background(), struct{}{}); err.Error() != openCircuitError { | ||
t.Fatalf("%s: want %q, have %q", caller, openCircuitError, err.Error()) | ||
} | ||
time.Sleep(requestDelay) | ||
} | ||
|
||
// Make sure none of those got through. | ||
if want, have := through, m.through; want != have { | ||
t.Errorf("%s: want %d, have %d", caller, want, have) | ||
} | ||
} | ||
|
||
type mock struct { | ||
through int | ||
err error | ||
} | ||
|
||
func (m *mock) endpoint(context.Context, interface{}) (interface{}, error) { | ||
m.through++ | ||
return struct{}{}, m.err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters