github.com/jxskiss/gopkg@v0.17.3/retry/breaker.go (about) 1 package retry 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 const windowSize = 10 9 const rollingRetryThreshold = 30 10 11 var breakerMap sync.Map 12 13 type breakerKey struct { 14 name string 15 overloadRatio float64 16 } 17 18 func getBreaker(name string, overloadRatio float64) *breaker { 19 key := breakerKey{ 20 name: name, 21 overloadRatio: overloadRatio, 22 } 23 w, ok := breakerMap.Load(key) 24 if !ok { 25 w = &breaker{ 26 ratio: overloadRatio, 27 } 28 breakerMap.Store(key, w) 29 } 30 return w.(*breaker) 31 } 32 33 type breaker struct { 34 ratio float64 35 succ bucket 36 fail bucket 37 } 38 39 func (b *breaker) shouldRetry() bool { 40 nowUnix := time.Now().Unix() 41 count := b.fail.sum(nowUnix) 42 if count > rollingRetryThreshold && 43 count > b.ratio*b.succ.sum(nowUnix) { 44 return false 45 } 46 return true 47 } 48 49 type bucket struct { 50 mu sync.RWMutex 51 index [windowSize]int64 52 count [windowSize]float64 53 } 54 55 func (b *bucket) incr(nowUnix int64, i float64) { 56 if i == 0 { 57 return 58 } 59 idx := nowUnix % windowSize 60 b.mu.Lock() 61 if b.index[idx] != nowUnix { 62 b.index[idx] = nowUnix 63 b.count[idx] = 0 64 } 65 b.count[idx] += i 66 b.mu.Unlock() 67 } 68 69 func (b *bucket) sum(nowUnix int64) float64 { 70 var sum float64 71 threshold := nowUnix - windowSize 72 b.mu.RLock() 73 for i := 0; i < windowSize; i++ { 74 if b.index[i] >= threshold { 75 sum += b.count[i] 76 } 77 } 78 b.mu.RUnlock() 79 return sum 80 }