Skip to content

Commit 93c3872

Browse files
committed
number of probes supported by the circuit breaker
1 parent 671274f commit 93c3872

File tree

2 files changed

+44
-7
lines changed

2 files changed

+44
-7
lines changed

core/circuitbreaker/circuit_breaker.go

+39-7
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ type circuitBreakerBase struct {
136136
retryTimeoutMs uint32
137137
// nextRetryTimestampMs is the time circuit breaker could probe
138138
nextRetryTimestampMs uint64
139+
// probeNumber is the number of probe requests that are allowed to pass when the circuit breaker is half open.
140+
probeNumber uint64
141+
// curProbeNumber is the real-time probe number.
142+
curProbeNumber uint64
139143
// state is the state machine of circuit breaker
140144
state *State
141145
}
@@ -156,6 +160,14 @@ func (b *circuitBreakerBase) updateNextRetryTimestamp() {
156160
atomic.StoreUint64(&b.nextRetryTimestampMs, util.CurrentTimeMillis()+uint64(b.retryTimeoutMs))
157161
}
158162

163+
func (b *circuitBreakerBase) addCurProbeNum() {
164+
atomic.AddUint64(&b.curProbeNumber, 1)
165+
}
166+
167+
func (b *circuitBreakerBase) resetCurProbeNum() {
168+
atomic.StoreUint64(&b.curProbeNumber, 0)
169+
}
170+
159171
// fromClosedToOpen updates circuit breaker state machine from closed to open.
160172
// Return true only if current goroutine successfully accomplished the transformation.
161173
func (b *circuitBreakerBase) fromClosedToOpen(snapshot interface{}) bool {
@@ -206,6 +218,7 @@ func (b *circuitBreakerBase) fromOpenToHalfOpen(ctx *base.EntryContext) bool {
206218
// Return true only if current goroutine successfully accomplished the transformation.
207219
func (b *circuitBreakerBase) fromHalfOpenToOpen(snapshot interface{}) bool {
208220
if b.state.cas(HalfOpen, Open) {
221+
b.resetCurProbeNum()
209222
b.updateNextRetryTimestamp()
210223
for _, listener := range stateChangeListeners {
211224
listener.OnTransformToOpen(HalfOpen, *b.rule, snapshot)
@@ -221,6 +234,7 @@ func (b *circuitBreakerBase) fromHalfOpenToOpen(snapshot interface{}) bool {
221234
// Return true only if current goroutine successfully accomplished the transformation.
222235
func (b *circuitBreakerBase) fromHalfOpenToClosed() bool {
223236
if b.state.cas(HalfOpen, Closed) {
237+
b.resetCurProbeNum()
224238
for _, listener := range stateChangeListeners {
225239
listener.OnTransformToClosed(HalfOpen, *b.rule)
226240
}
@@ -247,6 +261,7 @@ func newSlowRtCircuitBreakerWithStat(r *Rule, stat *slowRequestLeapArray) *slowR
247261
retryTimeoutMs: r.RetryTimeoutMs,
248262
nextRetryTimestampMs: 0,
249263
state: newState(),
264+
probeNumber: r.ProbeNum,
250265
},
251266
stat: stat,
252267
maxAllowedRt: r.MaxAllowedRtMs,
@@ -282,6 +297,8 @@ func (b *slowRtCircuitBreaker) TryPass(ctx *base.EntryContext) bool {
282297
if b.retryTimeoutArrived() && b.fromOpenToHalfOpen(ctx) {
283298
return true
284299
}
300+
} else if curStatus == HalfOpen && b.probeNumber > 0 {
301+
return true
285302
}
286303
return false
287304
}
@@ -318,9 +335,12 @@ func (b *slowRtCircuitBreaker) OnRequestComplete(rt uint64, _ error) {
318335
// fail to probe
319336
b.fromHalfOpenToOpen(1.0)
320337
} else {
321-
// succeed to probe
322-
b.fromHalfOpenToClosed()
323-
b.resetMetric()
338+
b.addCurProbeNum()
339+
if b.probeNumber == 0 || atomic.LoadUint64(&b.curProbeNumber) == b.probeNumber {
340+
// succeed to probe
341+
b.fromHalfOpenToClosed()
342+
b.resetMetric()
343+
}
324344
}
325345
return
326346
}
@@ -433,6 +453,7 @@ func newErrorRatioCircuitBreakerWithStat(r *Rule, stat *errorCounterLeapArray) *
433453
retryTimeoutMs: r.RetryTimeoutMs,
434454
nextRetryTimestampMs: 0,
435455
state: newState(),
456+
probeNumber: r.ProbeNum,
436457
},
437458
minRequestAmount: r.MinRequestAmount,
438459
errorRatioThreshold: r.Threshold,
@@ -465,6 +486,8 @@ func (b *errorRatioCircuitBreaker) TryPass(ctx *base.EntryContext) bool {
465486
if b.retryTimeoutArrived() && b.fromOpenToHalfOpen(ctx) {
466487
return true
467488
}
489+
} else if curStatus == HalfOpen && b.probeNumber > 0 {
490+
return true
468491
}
469492
return false
470493
}
@@ -498,8 +521,11 @@ func (b *errorRatioCircuitBreaker) OnRequestComplete(_ uint64, err error) {
498521
}
499522
if curStatus == HalfOpen {
500523
if err == nil {
501-
b.fromHalfOpenToClosed()
502-
b.resetMetric()
524+
b.addCurProbeNum()
525+
if b.probeNumber == 0 || atomic.LoadUint64(&b.curProbeNumber) == b.probeNumber {
526+
b.fromHalfOpenToClosed()
527+
b.resetMetric()
528+
}
503529
} else {
504530
b.fromHalfOpenToOpen(1.0)
505531
}
@@ -612,6 +638,7 @@ func newErrorCountCircuitBreakerWithStat(r *Rule, stat *errorCounterLeapArray) *
612638
retryTimeoutMs: r.RetryTimeoutMs,
613639
nextRetryTimestampMs: 0,
614640
state: newState(),
641+
probeNumber: r.ProbeNum,
615642
},
616643
minRequestAmount: r.MinRequestAmount,
617644
errorCountThreshold: uint64(r.Threshold),
@@ -644,6 +671,8 @@ func (b *errorCountCircuitBreaker) TryPass(ctx *base.EntryContext) bool {
644671
if b.retryTimeoutArrived() && b.fromOpenToHalfOpen(ctx) {
645672
return true
646673
}
674+
} else if curStatus == HalfOpen && b.probeNumber > 0 {
675+
return true
647676
}
648677
return false
649678
}
@@ -675,8 +704,11 @@ func (b *errorCountCircuitBreaker) OnRequestComplete(_ uint64, err error) {
675704
}
676705
if curStatus == HalfOpen {
677706
if err == nil {
678-
b.fromHalfOpenToClosed()
679-
b.resetMetric()
707+
b.addCurProbeNum()
708+
if b.probeNumber == 0 || atomic.LoadUint64(&b.curProbeNumber) == b.probeNumber {
709+
b.fromHalfOpenToClosed()
710+
b.resetMetric()
711+
}
680712
} else {
681713
b.fromHalfOpenToOpen(1)
682714
}

core/circuitbreaker/rule.go

+5
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ type Rule struct {
7878
// for ErrorRatio, it represents the max error request ratio
7979
// for ErrorCount, it represents the max error request count
8080
Threshold float64 `json:"threshold"`
81+
//ProbeNum is number of probes required when the circuit breaker is half-open.
82+
//when the probe num are set and circuit breaker in the half-open state.
83+
//if err occurs during the probe, the circuit breaker is opened immediately.
84+
//otherwise,the circuit breaker is closed only after the number of probes is reached
85+
ProbeNum uint64 `json:"probeNum"`
8186
}
8287

8388
func (r *Rule) String() string {

0 commit comments

Comments
 (0)