forked from zalando/skipper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
breaker.go
148 lines (120 loc) · 3.14 KB
/
breaker.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package circuit
import (
"strconv"
"strings"
"time"
)
// BreakerType defines the type of the used breaker: consecutive, rate or disabled.
type BreakerType int
const (
BreakerNone BreakerType = iota
ConsecutiveFailures
FailureRate
BreakerDisabled
)
// BreakerSettings contains the settings for individual circuit breakers.
//
// See the package overview for the detailed merging/overriding rules of the settings and for the meaning of the
// individual fields.
type BreakerSettings struct {
Type BreakerType
Host string
Window, Failures int
Timeout time.Duration
HalfOpenRequests int
IdleTTL time.Duration
}
type breakerImplementation interface {
Allow() (func(bool), bool)
}
type voidBreaker struct{}
// Breaker represents a single circuit breaker for a particular set of settings.
//
// Use the Get() method of the Registry to request fully initialized breakers.
type Breaker struct {
settings BreakerSettings
ts time.Time
impl breakerImplementation
}
func (to BreakerSettings) mergeSettings(from BreakerSettings) BreakerSettings {
if to.Type == BreakerNone {
to.Type = from.Type
if from.Type == ConsecutiveFailures {
to.Failures = from.Failures
}
if from.Type == FailureRate {
to.Window = from.Window
to.Failures = from.Failures
}
}
if to.Timeout == 0 {
to.Timeout = from.Timeout
}
if to.HalfOpenRequests == 0 {
to.HalfOpenRequests = from.HalfOpenRequests
}
if to.IdleTTL == 0 {
to.IdleTTL = from.IdleTTL
}
return to
}
// String returns the string representation of a particular set of settings.
func (s BreakerSettings) String() string {
var ss []string
switch s.Type {
case ConsecutiveFailures:
ss = append(ss, "type=consecutive")
case FailureRate:
ss = append(ss, "type=rate")
case BreakerDisabled:
return "disabled"
default:
return "none"
}
if s.Host != "" {
ss = append(ss, "host="+s.Host)
}
if s.Type == FailureRate && s.Window > 0 {
ss = append(ss, "window="+strconv.Itoa(s.Window))
}
if s.Failures > 0 {
ss = append(ss, "failures="+strconv.Itoa(s.Failures))
}
if s.Timeout > 0 {
ss = append(ss, "timeout="+s.Timeout.String())
}
if s.HalfOpenRequests > 0 {
ss = append(ss, "half-open-requests="+strconv.Itoa(s.HalfOpenRequests))
}
if s.IdleTTL > 0 {
ss = append(ss, "idle-ttl="+s.IdleTTL.String())
}
return strings.Join(ss, ",")
}
func (b voidBreaker) Allow() (func(bool), bool) {
return func(bool) {}, true
}
func newBreaker(s BreakerSettings) *Breaker {
var impl breakerImplementation
switch s.Type {
case ConsecutiveFailures:
impl = newConsecutive(s)
case FailureRate:
impl = newRate(s)
default:
impl = voidBreaker{}
}
return &Breaker{
settings: s,
impl: impl,
}
}
// Allow returns true if the breaker is in the closed state and a callback function for reporting the outcome of
// the operation. The callback expects true values if the outcome of the request was successful. Allow may not
// return a callback function when the state is open.
func (b *Breaker) Allow() (func(bool), bool) {
return b.impl.Allow()
}
func (b *Breaker) idle(now time.Time) bool {
return now.Sub(b.ts) > b.settings.IdleTTL
}