-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgooglebreaker.go
97 lines (75 loc) · 1.69 KB
/
googlebreaker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package breaker
import (
"math/rand"
"time"
"github.com/chenyanchen/breaker/internal/rollingwindow"
)
const (
defaultK = 1.5
defaultSize = 20
defaultInterval = time.Millisecond * 500
)
type googleBreaker struct {
rand *rand.Rand
k float64
stat *rollingwindow.RollingWindow
}
type Option func(*googleBreaker)
func WithK(k float64) Option {
return func(b *googleBreaker) { b.k = k }
}
func WithWindow(size int, interval time.Duration) Option {
return func(b *googleBreaker) {
b.stat = rollingwindow.NewRollingWindow(size, interval)
}
}
func NewGoogleBreaker(opts ...Option) *googleBreaker {
b := &googleBreaker{
k: defaultK,
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
stat: rollingwindow.NewRollingWindow(defaultSize, defaultInterval),
}
for _, opt := range opts {
opt(b)
}
return b
}
func (b *googleBreaker) Do(f func() error) error {
if err := b.accept(); err != nil {
return err
}
defer func() {
if v := recover(); v != nil {
b.markFailure()
panic(v)
}
}()
err := f()
if err != nil {
b.markFailure()
} else {
b.markSuccess()
}
return err
}
func (b *googleBreaker) accept() error {
accepts, requests := b.history()
// https://sre.google/sre-book/handling-overload/#eq2101
dropRatio := (requests - b.k*accepts) / (requests + 1)
if dropRatio <= 0 {
return nil
}
if b.rand.Float64() < dropRatio {
return ErrServiceUnavailable
}
return nil
}
func (b *googleBreaker) markSuccess() { b.stat.Add(1) }
func (b *googleBreaker) markFailure() { b.stat.Add(0) }
func (b *googleBreaker) history() (accepts, requests float64) {
b.stat.Reduce(func(b *rollingwindow.Bucket) {
accepts += b.Value
requests += b.Count
})
return
}