From b5e5d68098a2807f6b0df99c4d27a8e79411af8c Mon Sep 17 00:00:00 2001 From: tian Date: Thu, 5 Jul 2018 17:11:58 +0800 Subject: [PATCH 1/2] add Flush by name, support force closed options --- hystrix/circuit.go | 39 ++++++++++++++++++++++++++++++++++++++- hystrix/settings.go | 3 +++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/hystrix/circuit.go b/hystrix/circuit.go index 87d88b9..899a595 100644 --- a/hystrix/circuit.go +++ b/hystrix/circuit.go @@ -5,6 +5,7 @@ import ( "sync" "sync/atomic" "time" + "errors" ) // CircuitBreaker is created for each ExecutorPool to track whether requests @@ -13,6 +14,7 @@ type CircuitBreaker struct { Name string open bool forceOpen bool + forceClosed bool mutex *sync.RWMutex openedOrLastTestedTime int64 @@ -20,6 +22,11 @@ type CircuitBreaker struct { metrics *metricExchange } +var ( + // ErrCBNotExist occurs when no CircuitBreaker exists + ErrCBNotExist = errors.New("circuit breaker not exist") +) + var ( circuitBreakersMutex *sync.RWMutex circuitBreakers map[string]*CircuitBreaker @@ -30,6 +37,17 @@ func init() { circuitBreakers = make(map[string]*CircuitBreaker) } +// IsCircuitBreakerOpen returns whether a circuitBreaker is open for an interface +func IsCircuitBreakerOpen(name string) (bool, error) { + circuitBreakersMutex.Lock() + defer circuitBreakersMutex.Unlock() + if c, ok := circuitBreakers[name]; ok { + return c.IsOpen(), nil + } else { + return false, ErrCBNotExist + } +} + // GetCircuit returns the circuit for the given command and whether this call created it. func GetCircuit(name string) (*CircuitBreaker, bool, error) { circuitBreakersMutex.RLock() @@ -51,6 +69,17 @@ func GetCircuit(name string) (*CircuitBreaker, bool, error) { return circuitBreakers[name], !ok, nil } +func FlushByName(name string) { + circuitBreakersMutex.Lock() + defer circuitBreakersMutex.Unlock() + cb, ok := circuitBreakers[name] + if ok { + cb.metrics.Reset() + cb.executorPool.Metrics.Reset() + delete(circuitBreakers, name) + } + +} // Flush purges all circuit and metric information from memory. func Flush() { @@ -71,7 +100,8 @@ func newCircuitBreaker(name string) *CircuitBreaker { c.metrics = newMetricExchange(name) c.executorPool = newExecutorPool(name) c.mutex = &sync.RWMutex{} - + c.forceOpen = getSettings(name).ForceOpen + c.forceClosed = getSettings(name).ForceClose return c } @@ -115,6 +145,13 @@ func (circuit *CircuitBreaker) IsOpen() bool { // When the circuit is open, this call will occasionally return true to measure whether the external service // has recovered. func (circuit *CircuitBreaker) AllowRequest() bool { + //force open has highest priority + if circuit.forceOpen { + return false + } + if circuit.forceClosed { + return true + } return !circuit.IsOpen() || circuit.allowSingleTest() } diff --git a/hystrix/settings.go b/hystrix/settings.go index 8a7d47f..cfdcb72 100644 --- a/hystrix/settings.go +++ b/hystrix/settings.go @@ -26,6 +26,9 @@ type Settings struct { RequestVolumeThreshold uint64 SleepWindow time.Duration ErrorPercentThreshold int + ForceFallback bool + ForceOpen bool + ForceClose bool } // CommandConfig is used to tune circuit settings at runtime From 3fc47cdab25a67af354953725c2e2ba5e97da32b Mon Sep 17 00:00:00 2001 From: Shawn Date: Fri, 26 Oct 2018 17:46:56 +0800 Subject: [PATCH 2/2] fix go routine massive increase, if there is multiple collector and high concurrency, it will cause too much GC --- hystrix/metrics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hystrix/metrics.go b/hystrix/metrics.go index d289fe6..01e6c0d 100644 --- a/hystrix/metrics.go +++ b/hystrix/metrics.go @@ -58,7 +58,7 @@ func (m *metricExchange) Monitor() { wg := &sync.WaitGroup{} for _, collector := range m.metricCollectors { wg.Add(1) - go m.IncrementMetrics(wg, collector, update, totalDuration) + m.IncrementMetrics(wg, collector, update, totalDuration) } wg.Wait()