From 221ad18bdacd58b08806f1a926e81f698f48d74b Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Wed, 3 Apr 2019 16:11:56 +0530 Subject: [PATCH 1/5] Added Callback in circuit breaker --- hystrix/callback/callback.go | 34 ++++++++++++++++++ hystrix/callback/callback_test.go | 57 +++++++++++++++++++++++++++++++ hystrix/circuit.go | 11 +++++- hystrix/settings.go | 2 ++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 hystrix/callback/callback.go create mode 100644 hystrix/callback/callback_test.go diff --git a/hystrix/callback/callback.go b/hystrix/callback/callback.go new file mode 100644 index 0000000..de110bc --- /dev/null +++ b/hystrix/callback/callback.go @@ -0,0 +1,34 @@ +package callback + +var circuitCallback map[string]stateFunc + +//State is a type to hold Circuit-state this will be used while calling stateFunc on State change +type State string + +const ( + //Open is a state to indicate that Circuit state is Open + Open = "Open" + //Close is a state to indicate that Circuit state is Close + Close = "Close" + //AllowSingle is a state to indicate that Circuit state is AllowSingle or trying to Open Circuit + AllowSingle = "Allow Single" +) + +type stateFunc func(name string, state State) + +func init() { + circuitCallback = make(map[string]stateFunc) +} + +//Register adds callback for a circuit +func Register(name string, callbackFunc stateFunc) { + circuitCallback[name] = callbackFunc +} + +//Invoke is a function to invoke Callback function in a goroutine on State change +func Invoke(name string, state State) { + callbackFunc, _ := circuitCallback[name] + if callbackFunc != nil { + go callbackFunc(name, state) + } +} diff --git a/hystrix/callback/callback_test.go b/hystrix/callback/callback_test.go new file mode 100644 index 0000000..6750a28 --- /dev/null +++ b/hystrix/callback/callback_test.go @@ -0,0 +1,57 @@ +package callback + +import ( + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestRegister(t *testing.T) { + Convey("Register a command", t, func() { + Register("Test-Command", func(name string, state State) {}) + + Convey("Read Callback function for Registered Command", func() { + callbackFunc, _ := circuitCallback["Test-Command"] + So(callbackFunc, ShouldNotBeNil) + }) + Convey("Read Callback function for unknown Command", func() { + callbackFunc, _ := circuitCallback["Command"] + So(callbackFunc, ShouldBeNil) + }) + }) +} + +func TestInvoke(t *testing.T) { + Convey("Register a command", t, func() { + var callbackInvoked bool + var callbackState State = Close + Register("TestInvokeCommand", func(name string, state State) { + callbackInvoked = true + callbackState = state + }) + + Invoke("TestInvokeCommand", Open) + time.Sleep(2 * time.Second) + Convey("Invoke Callback for Registered Command", func() { + So(callbackInvoked, ShouldBeTrue) + So(callbackState, ShouldEqual, Open) + }) + }) + + Convey("Register a Invoke command", t, func() { + var callbackInvoked = false + var callbackState State = Close + Register("TestInvokeCommand", func(name string, state State) { + callbackInvoked = true + callbackState = state + }) + + Invoke("Command", Open) + + Convey("Read Callback function for unknown Command", func() { + So(callbackInvoked, ShouldBeFalse) + So(callbackState, ShouldEqual, Close) + }) + }) +} diff --git a/hystrix/circuit.go b/hystrix/circuit.go index 87d88b9..76cdd7a 100644 --- a/hystrix/circuit.go +++ b/hystrix/circuit.go @@ -5,6 +5,8 @@ import ( "sync" "sync/atomic" "time" + + "github.com/ContinuumLLC/hystrix-go/hystrix/callback" ) // CircuitBreaker is created for each ExecutorPool to track whether requests @@ -128,6 +130,8 @@ func (circuit *CircuitBreaker) allowSingleTest() bool { swapped := atomic.CompareAndSwapInt64(&circuit.openedOrLastTestedTime, openedOrLastTestedTime, now) if swapped { log.Printf("hystrix-go: allowing single test to possibly close circuit %v", circuit.Name) + + callback.Invoke(circuit.Name, callback.AllowSingle) } return swapped } @@ -144,9 +148,11 @@ func (circuit *CircuitBreaker) setOpen() { } log.Printf("hystrix-go: opening circuit %v", circuit.Name) - circuit.openedOrLastTestedTime = time.Now().UnixNano() circuit.open = true + + callback.Invoke(circuit.Name, callback.Open) + } func (circuit *CircuitBreaker) setClose() { @@ -161,6 +167,9 @@ func (circuit *CircuitBreaker) setClose() { circuit.open = false circuit.metrics.Reset() + + callback.Invoke(circuit.Name, callback.Close) + } // ReportEvent records command metrics for tracking recent error rates and exposing data to the dashboard. diff --git a/hystrix/settings.go b/hystrix/settings.go index 8a7d47f..5310f58 100644 --- a/hystrix/settings.go +++ b/hystrix/settings.go @@ -20,6 +20,7 @@ var ( DefaultLogger = NoopLogger{} ) +//Settings is used to tune circuit settings type Settings struct { Timeout time.Duration MaxConcurrentRequests int @@ -106,6 +107,7 @@ func getSettings(name string) *Settings { return s } +//GetCircuitSettings returns Circuit Settings for each command func GetCircuitSettings() map[string]*Settings { copy := make(map[string]*Settings) From 4f7f0a216ae56fb0ef5f800521762de684b5598f Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Wed, 3 Apr 2019 16:20:03 +0530 Subject: [PATCH 2/5] Updated Import statement --- hystrix/circuit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hystrix/circuit.go b/hystrix/circuit.go index 76cdd7a..dbc5fbe 100644 --- a/hystrix/circuit.go +++ b/hystrix/circuit.go @@ -6,7 +6,7 @@ import ( "sync/atomic" "time" - "github.com/ContinuumLLC/hystrix-go/hystrix/callback" + "github.com/afex/hystrix-go/hystrix/callback" ) // CircuitBreaker is created for each ExecutorPool to track whether requests From d82962fc32a89f7da2a40ac81f8259edbf944ade Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Wed, 3 Apr 2019 18:51:45 +0530 Subject: [PATCH 3/5] Updated readme file --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 981ab50..a009ac7 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,15 @@ hystrix.ConfigureCommand("my_command", hystrix.CommandConfig{ You can also use ```hystrix.Configure()``` which accepts a ```map[string]CommandConfig```. + +### Enable State Change Callback +In your main.go, register the Callback handler for a command which will be called in a goroutine. + +```go +callback.Register("my_command", func(name string, state callback.State) { + fmt.Println("Name ", name, " State ", state) +}) +``` ### Enable dashboard metrics In your main.go, register the event stream HTTP handler on a port and launch it in a goroutine. Once you configure turbine for your [Hystrix Dashboard](https://github.com/Netflix/Hystrix/tree/master/hystrix-dashboard) to start streaming events, your commands will automatically begin appearing. From 575c1872aaa7cf4a68fb506367fb75d91644c20c Mon Sep 17 00:00:00 2001 From: gpancorvo Date: Thu, 8 Oct 2020 13:18:31 -0400 Subject: [PATCH 4/5] add go.mod --- go.mod | 13 +++++++++++++ go.sum | 31 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..96a1a4a --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/afex/hystrix-go + +go 1.15 + +require ( + github.com/DataDog/datadog-go v4.0.1+incompatible + github.com/cactus/go-statsd-client/statsd v0.0.0-20200728222731-a2baea3bbfc6 + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 + github.com/smartystreets/goconvey v1.6.4 + github.com/stretchr/testify v1.6.1 // indirect +) + +replace github.com/afex/hystrix-go => github.com/ContinuumLLC/hystrix-go v1.0.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ed002e1 --- /dev/null +++ b/go.sum @@ -0,0 +1,31 @@ +github.com/DataDog/datadog-go v4.0.1+incompatible h1:6BF/mPNV2f+PJzpw5vxDK91Z3IfOM7fEmz5ptuL+0vo= +github.com/DataDog/datadog-go v4.0.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/cactus/go-statsd-client/statsd v0.0.0-20200728222731-a2baea3bbfc6 h1:arL5CcymFtWyyi5Xk44VpSxUfFfJqxCMbhZQ0lxLkDE= +github.com/cactus/go-statsd-client/statsd v0.0.0-20200728222731-a2baea3bbfc6/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 7b4e784eb793f5551d628b70186ed0a95429f079 Mon Sep 17 00:00:00 2001 From: gpancorvo Date: Wed, 14 Oct 2020 08:40:55 -0400 Subject: [PATCH 5/5] point to 1.0.1 fork version --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 96a1a4a..51e5057 100644 --- a/go.mod +++ b/go.mod @@ -10,4 +10,4 @@ require ( github.com/stretchr/testify v1.6.1 // indirect ) -replace github.com/afex/hystrix-go => github.com/ContinuumLLC/hystrix-go v1.0.0 +replace github.com/afex/hystrix-go => github.com/ContinuumLLC/hystrix-go v1.0.1