123 lines
2.3 KiB
Go
123 lines
2.3 KiB
Go
package nixmsg
|
|
|
|
import (
|
|
"math/rand"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// reconnectBackoff 按 DEVELOPMENT 第 9 节:1s 起、加倍、上限 30s、±30% 抖动;
|
|
// 稳定在线 60s 后恢复到 1s。
|
|
type reconnectBackoff struct {
|
|
mu sync.Mutex
|
|
base time.Duration
|
|
onlineAt time.Time
|
|
online bool
|
|
stable bool
|
|
timer *time.Timer
|
|
}
|
|
|
|
func newReconnectBackoff() *reconnectBackoff {
|
|
return &reconnectBackoff{base: time.Second}
|
|
}
|
|
|
|
func (b *reconnectBackoff) Func(attempt int) time.Duration {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if attempt <= 0 {
|
|
return 0
|
|
}
|
|
d := b.base
|
|
for i := 1; i < attempt; i++ {
|
|
d *= 2
|
|
if d > 30*time.Second {
|
|
d = 30 * time.Second
|
|
break
|
|
}
|
|
}
|
|
return withJitter(d)
|
|
}
|
|
|
|
func (b *reconnectBackoff) MarkOnline() {
|
|
b.mu.Lock()
|
|
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
|
|
}
|
|
})
|
|
}
|
|
|
|
func (b *reconnectBackoff) MarkOffline() {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if b.timer != nil {
|
|
b.timer.Stop()
|
|
b.timer = nil
|
|
}
|
|
wasOnline := b.online
|
|
b.online = false
|
|
if !wasOnline {
|
|
// 连接尚未成功就失败:在 Func 内已按 attempt 加倍,这里把 base 提到下次周期的起点。
|
|
next := b.base * 2
|
|
if next > 30*time.Second {
|
|
next = 30 * time.Second
|
|
}
|
|
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
|
|
return
|
|
}
|
|
next := b.base * 2
|
|
if next > 30*time.Second {
|
|
next = 30 * time.Second
|
|
}
|
|
b.base = next
|
|
b.stable = false
|
|
}
|
|
|
|
func (b *reconnectBackoff) Base() time.Duration {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.base
|
|
}
|
|
|
|
func withJitter(d time.Duration) time.Duration {
|
|
// ±30%
|
|
f := 0.7 + rand.Float64()*0.6
|
|
return time.Duration(float64(d) * f)
|
|
}
|
|
|
|
// 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
|
|
}
|