fix: 按 K-00 约定修复 Go SDK 断线重交与退避
This commit is contained in:
+84
-73
@@ -6,36 +6,76 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// reconnectBackoff 按 DEVELOPMENT 第 9 节:1s 起、加倍、上限 30s、±30% 抖动;
|
||||
// 稳定在线 60s 后恢复到 1s。
|
||||
// 重连 / 限速重交共用标称间隔:第 n 次 min(1s×2^(n-1), 30s),n≥1。
|
||||
func nominalDelay(n int) time.Duration {
|
||||
if n < 1 {
|
||||
return 0
|
||||
}
|
||||
if n > 6 {
|
||||
return 30 * time.Second
|
||||
}
|
||||
d := time.Second
|
||||
for i := 1; i < n; i++ {
|
||||
d *= 2
|
||||
if d >= 30*time.Second {
|
||||
return 30 * time.Second
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
var backoffJitter = withJitter
|
||||
|
||||
func withJitter(d time.Duration) time.Duration {
|
||||
if d <= 0 {
|
||||
return 0
|
||||
}
|
||||
f := 0.7 + rand.Float64()*0.6
|
||||
return time.Duration(float64(d) * f)
|
||||
}
|
||||
|
||||
// reconnectBackoff 只维护一个连续失败计数 n。
|
||||
// 应用调用 Connect 后的第一次连接不等待;之后第 n 次等待 nominalDelay(n)×抖动。
|
||||
type reconnectBackoff struct {
|
||||
mu sync.Mutex
|
||||
base time.Duration
|
||||
onlineAt time.Time
|
||||
online bool
|
||||
stable bool
|
||||
timer *time.Timer
|
||||
mu sync.Mutex
|
||||
n int
|
||||
skipFirst bool
|
||||
online bool
|
||||
onlineAt time.Time
|
||||
counted bool
|
||||
}
|
||||
|
||||
func newReconnectBackoff() *reconnectBackoff {
|
||||
return &reconnectBackoff{base: time.Second}
|
||||
return &reconnectBackoff{skipFirst: true}
|
||||
}
|
||||
|
||||
// Func 供 autopaho;忽略其 attempt,只用本对象的 n。
|
||||
func (b *reconnectBackoff) Func(attempt int) time.Duration {
|
||||
_ = attempt
|
||||
return b.NextWait()
|
||||
}
|
||||
|
||||
func (b *reconnectBackoff) NextWait() time.Duration {
|
||||
return b.nextWait(true)
|
||||
}
|
||||
|
||||
func (b *reconnectBackoff) nextWait(jitter bool) time.Duration {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if attempt <= 0 {
|
||||
b.counted = false
|
||||
if b.skipFirst {
|
||||
b.skipFirst = false
|
||||
return 0
|
||||
}
|
||||
d := b.base
|
||||
for i := 1; i < attempt; i++ {
|
||||
d *= 2
|
||||
if d > 30*time.Second {
|
||||
d = 30 * time.Second
|
||||
break
|
||||
}
|
||||
n := b.n
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
return withJitter(d)
|
||||
d := nominalDelay(n)
|
||||
if jitter {
|
||||
return backoffJitter(d)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func (b *reconnectBackoff) MarkOnline() {
|
||||
@@ -43,80 +83,51 @@ func (b *reconnectBackoff) MarkOnline() {
|
||||
defer b.mu.Unlock()
|
||||
b.online = true
|
||||
b.onlineAt = time.Now()
|
||||
b.stable = false
|
||||
if b.timer != nil {
|
||||
b.timer.Stop()
|
||||
}
|
||||
b.timer = time.AfterFunc(60*time.Second, func() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.online {
|
||||
b.stable = true
|
||||
b.base = time.Second
|
||||
}
|
||||
})
|
||||
b.counted = false
|
||||
}
|
||||
|
||||
func (b *reconnectBackoff) MarkOffline() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.timer != nil {
|
||||
b.timer.Stop()
|
||||
b.timer = nil
|
||||
if b.counted {
|
||||
return
|
||||
}
|
||||
wasOnline := b.online
|
||||
b.counted = true
|
||||
was := b.online
|
||||
onlineAt := b.onlineAt
|
||||
b.online = false
|
||||
if !wasOnline {
|
||||
// 连接尚未成功就失败:在 Func 内已按 attempt 加倍,这里把 base 提到下次周期的起点。
|
||||
next := b.base * 2
|
||||
if next > 30*time.Second {
|
||||
next = 30 * time.Second
|
||||
if !was {
|
||||
b.n++
|
||||
if b.n < 1 {
|
||||
b.n = 1
|
||||
}
|
||||
if next < time.Second {
|
||||
next = time.Second
|
||||
}
|
||||
b.base = next
|
||||
return
|
||||
}
|
||||
if b.stable || time.Since(b.onlineAt) >= 60*time.Second {
|
||||
b.base = time.Second
|
||||
b.stable = false
|
||||
if time.Since(onlineAt) >= 60*time.Second {
|
||||
b.n = 1
|
||||
return
|
||||
}
|
||||
next := b.base * 2
|
||||
if next > 30*time.Second {
|
||||
next = 30 * time.Second
|
||||
b.n++
|
||||
if b.n < 1 {
|
||||
b.n = 1
|
||||
}
|
||||
b.base = next
|
||||
b.stable = false
|
||||
}
|
||||
|
||||
func (b *reconnectBackoff) Base() time.Duration {
|
||||
func (b *reconnectBackoff) N() int {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.base
|
||||
return b.n
|
||||
}
|
||||
|
||||
func withJitter(d time.Duration) time.Duration {
|
||||
// ±30%
|
||||
f := 0.7 + rand.Float64()*0.6
|
||||
return time.Duration(float64(d) * f)
|
||||
func (b *reconnectBackoff) setOnlineAtForTest(t time.Time) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.online = true
|
||||
b.onlineAt = t
|
||||
}
|
||||
|
||||
// computeBackoffDelay 供单测:无 attempt 与 base 计算无抖动前的标称延迟。
|
||||
func computeBackoffDelay(base time.Duration, attempt int) time.Duration {
|
||||
if attempt <= 0 {
|
||||
return 0
|
||||
}
|
||||
d := base
|
||||
for i := 1; i < attempt; i++ {
|
||||
d *= 2
|
||||
if d > 30*time.Second {
|
||||
return 30 * time.Second
|
||||
}
|
||||
}
|
||||
if d > 30*time.Second {
|
||||
return 30 * time.Second
|
||||
}
|
||||
return d
|
||||
// computeBackoffDelay 保留给旧单测:忽略 base,按 n 计算标称延迟。
|
||||
func computeBackoffDelay(base time.Duration, n int) time.Duration {
|
||||
_ = base
|
||||
return nominalDelay(n)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user