1
+ // Copyright (c) 2015-present Jeevanandam M ([email protected] ), All rights reserved.
2
+ // resty source code and usage is governed by a MIT style
3
+ // license that can be found in the LICENSE file.
4
+ // SPDX-License-Identifier: MIT
5
+
1
6
package resty
2
7
3
8
import (
@@ -7,73 +12,92 @@ import (
7
12
"time"
8
13
)
9
14
10
- // CircuitBreaker can be in one of three states: Closed, Open, or Half-Open.
11
- // - When the CircuitBreaker is Closed, requests are allowed to pass through.
12
- // - If a failure count threshold is reached within a specified time-frame,
13
- // the CircuitBreaker transitions to the Open state.
14
- // - When the CircuitBreaker is Open, requests are blocked.
15
- // - After a specified timeout, the CircuitBreaker transitions to the Half-Open state.
16
- // - When the CircuitBreaker is Half-Open, a single request is allowed to pass through.
17
- // - If that request fails, the CircuitBreaker returns to the Open state.
18
- // - If the number of successes reaches a specified threshold,
19
- // the CircuitBreaker transitions to the Closed state.
15
+ // CircuitBreaker struct implements a state machine to monitor and manage the
16
+ // states of circuit breakers. The three states are:
17
+ // - Closed: requests are allowed
18
+ // - Open: requests are blocked
19
+ // - Half-Open: a single request is allowed to determine
20
+ //
21
+ // Transitions
22
+ // - To Closed State: when the success count reaches the success threshold.
23
+ // - To Open State: when the failure count reaches the failure threshold.
24
+ // - Half-Open Check: when the specified timeout reaches, a single request is allowed
25
+ // to determine the transition state; if failed, it goes back to the open state.
20
26
type CircuitBreaker struct {
21
- policies []CircuitBreakerPolicy
22
- timeout time.Duration
23
- failThreshold , successThreshold uint32
24
-
25
- state atomic.Value // circuitBreakerState
26
- failCount , successCount atomic.Uint32
27
- lastFail time.Time
27
+ policies []CircuitBreakerPolicy
28
+ timeout time.Duration
29
+ failureThreshold uint32
30
+ successThreshold uint32
31
+ state atomic.Value // circuitBreakerState
32
+ failureCount atomic.Uint32
33
+ successCount atomic.Uint32
34
+ lastFailureAt time.Time
28
35
}
29
36
30
- // NewCircuitBreaker creates a new [CircuitBreaker] with default settings.
37
+ // NewCircuitBreaker method creates a new [CircuitBreaker] with default settings.
38
+ //
31
39
// The default settings are:
32
- // - Timeout: 10 seconds
33
- // - FailThreshold: 3
34
- // - SuccessThreshold: 1
35
- // - Policies: CircuitBreaker5xxPolicy
40
+ // - Timeout: 10 seconds
41
+ // - FailThreshold: 3
42
+ // - SuccessThreshold: 1
43
+ // - Policies: CircuitBreaker5xxPolicy
36
44
func NewCircuitBreaker () * CircuitBreaker {
37
45
cb := & CircuitBreaker {
38
46
policies : []CircuitBreakerPolicy {CircuitBreaker5xxPolicy },
39
47
timeout : 10 * time .Second ,
40
- failThreshold : 3 ,
48
+ failureThreshold : 3 ,
41
49
successThreshold : 1 ,
42
50
}
43
51
cb .state .Store (circuitBreakerStateClosed )
44
52
return cb
45
53
}
46
54
47
- // SetPolicies sets the CircuitBreakerPolicy's that the [CircuitBreaker] will use to determine whether a response is a failure.
48
- func (cb * CircuitBreaker ) SetPolicies (policies []CircuitBreakerPolicy ) * CircuitBreaker {
55
+ // SetPolicies method sets the one or more given CircuitBreakerPolicy(s) into
56
+ // [CircuitBreaker], which will be used to determine whether a request is failed
57
+ // or successful by evaluating the response instance.
58
+ //
59
+ // // set one policy
60
+ // cb.SetPolicies(CircuitBreaker5xxPolicy)
61
+ //
62
+ // // set multiple polices
63
+ // cb.SetPolicies(policy1, policy2, policy3)
64
+ //
65
+ // // if you have slice, do
66
+ // cb.SetPolicies(policies...)
67
+ //
68
+ // NOTE: This method overwrites the policies with the given new ones. See [CircuitBreaker.AddPolicies]
69
+ func (cb * CircuitBreaker ) SetPolicies (policies ... CircuitBreakerPolicy ) * CircuitBreaker {
49
70
cb .policies = policies
50
71
return cb
51
72
}
52
73
53
- // SetTimeout sets the timeout duration for the [CircuitBreaker].
74
+ // SetTimeout method sets the timeout duration for the [CircuitBreaker]. When the
75
+ // timeout reaches, a single request is allowed to determine the state.
54
76
func (cb * CircuitBreaker ) SetTimeout (timeout time.Duration ) * CircuitBreaker {
55
77
cb .timeout = timeout
56
78
return cb
57
79
}
58
80
59
- // SetFailThreshold sets the number of failures that must occur within the timeout duration for the [CircuitBreaker] to
60
- // transition to the Open state.
61
- func (cb * CircuitBreaker ) SetFailThreshold (threshold uint32 ) * CircuitBreaker {
62
- cb .failThreshold = threshold
81
+ // SetFailureThreshold method sets the number of failures that must occur within the
82
+ // timeout duration for the [CircuitBreaker] to transition to the Open state.
83
+ func (cb * CircuitBreaker ) SetFailureThreshold (threshold uint32 ) * CircuitBreaker {
84
+ cb .failureThreshold = threshold
63
85
return cb
64
86
}
65
87
66
- // SetSuccessThreshold sets the number of successes that must occur to transition the [CircuitBreaker] from the Half-Open state
67
- // to the Closed state.
88
+ // SetSuccessThreshold method sets the number of successes that must occur to transition
89
+ // the [CircuitBreaker] from the Half-Open state to the Closed state.
68
90
func (cb * CircuitBreaker ) SetSuccessThreshold (threshold uint32 ) * CircuitBreaker {
69
91
cb .successThreshold = threshold
70
92
return cb
71
93
}
72
94
73
- // CircuitBreakerPolicy is a function that determines whether a response should trip the [CircuitBreaker].
95
+ // CircuitBreakerPolicy is a function type that determines whether a response should
96
+ // trip the [CircuitBreaker].
74
97
type CircuitBreakerPolicy func (resp * http.Response ) bool
75
98
76
- // CircuitBreaker5xxPolicy is a [CircuitBreakerPolicy] that trips the [CircuitBreaker] if the response status code is 500 or greater.
99
+ // CircuitBreaker5xxPolicy is a [CircuitBreakerPolicy] that trips the [CircuitBreaker] if
100
+ // the response status code is 500 or greater.
77
101
func CircuitBreaker5xxPolicy (resp * http.Response ) bool {
78
102
return resp .StatusCode > 499
79
103
}
@@ -118,17 +142,17 @@ func (cb *CircuitBreaker) applyPolicies(resp *http.Response) {
118
142
}
119
143
120
144
if failed {
121
- if cb .failCount .Load () > 0 && time .Since (cb .lastFail ) > cb .timeout {
122
- cb .failCount .Store (0 )
145
+ if cb .failureCount .Load () > 0 && time .Since (cb .lastFailureAt ) > cb .timeout {
146
+ cb .failureCount .Store (0 )
123
147
}
124
148
125
149
switch cb .getState () {
126
150
case circuitBreakerStateClosed :
127
- failCount := cb .failCount .Add (1 )
128
- if failCount >= cb .failThreshold {
151
+ failCount := cb .failureCount .Add (1 )
152
+ if failCount >= cb .failureThreshold {
129
153
cb .open ()
130
154
} else {
131
- cb .lastFail = time .Now ()
155
+ cb .lastFailureAt = time .Now ()
132
156
}
133
157
case circuitBreakerStateHalfOpen :
134
158
cb .open ()
@@ -144,8 +168,6 @@ func (cb *CircuitBreaker) applyPolicies(resp *http.Response) {
144
168
}
145
169
}
146
170
}
147
-
148
- return
149
171
}
150
172
151
173
func (cb * CircuitBreaker ) open () {
@@ -157,7 +179,7 @@ func (cb *CircuitBreaker) open() {
157
179
}
158
180
159
181
func (cb * CircuitBreaker ) changeState (state circuitBreakerState ) {
160
- cb .failCount .Store (0 )
182
+ cb .failureCount .Store (0 )
161
183
cb .successCount .Store (0 )
162
184
cb .state .Store (state )
163
185
}
0 commit comments