feat(qq-obv11): client 模式首连失败不再致命(复用既有重连循环 + 指数退避)
痛点:mode=client 时首次拨号失败会让 Connect 直接返回 error ⇒ 渠道起不来; 而「连上后再断」本来就有重连。⇒ 把 readLoop 的入口条件放宽为「conn==nil 也算」, **不新造第二套循环**。 新增 reconnect.go:退避 2s → ×2 → 上限 60s、抖动 ±25%(具名常量),失败计数跨 Connect 与 readLoop 共享。 ws_client:新增 connectFirstClient —— 可重试失败只记 WARN 并返回 nil; 配置缺失(remote_url 空)仍致命(闸门口径不变)。 connected 语义不许撒谎:始终由真实连接决定;HealthCheck 未连上返回 ErrNotConnected; 仅 http 传输保留「已装配即可服务」旧口径(逐字未改)。 ctx 感知等待(sleepCtx)+ 等待后复查 ctx ⇒ Disconnect 能立即中止,不泄漏 goroutine。 server 模式与 http 三端行为逐字不变(既有用例零改动全绿 + TestServerModeUnaffectedByClientRetry)。 测试:client_retry_test.go(428) + reconnect_test.go(83),用可注入 dialer + gate 通道造确定性时序。 变异 5 条(首连 fail-fast / 退避无上限 / 取消失效 / 未连上就报已连 / 抖动未接线)。 诚实边界:60s 上限对聊天渠道可能偏慢(改 30s 是一行常量);仓库内仍存第二份指数退避 (wsutil.Backoff 无抖动能力,收敛需改 internal/**,本切片禁碰)。
This commit is contained in:
1 parent
d804c4e675
commit
8c437866e7
6 files changed
+698
-10
No files matched your search
@@ -117,6 +117,10 @@ type Adapter struct {
|
||||
groups map[string]string
|
||||
|
||||
lastSend time.Time
|
||||
|
||||
// reconnect / policy 只服务 ws+client 的首连与重连退避(见 reconnect.go)。
|
||||
reconnect reconnectState
|
||||
policy reconnectPolicy
|
||||
}
|
||||
|
||||
// Compile-time conformance with the channel contract.
|
||||
@@ -144,6 +148,7 @@ func NewAdapter(cfg Config) *Adapter {
|
||||
groups: map[string]string{},
|
||||
newConn: make(chan struct{}, 1),
|
||||
limiter: newRateLimiter(cfg.MaxProactivePerWindow, cfg.ProactiveWindow),
|
||||
policy: defaultReconnectPolicy(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +173,8 @@ func (a *Adapter) Config() map[string]any {
|
||||
}
|
||||
|
||||
// Connect establishes the session for the configured transport × mode. It is
|
||||
// idempotent.
|
||||
// idempotent. ws+client 的首连失败**不致命**(对面可能比 Cyrene 后启动):
|
||||
// Connect 照常返回 nil,重试交给 readLoop 的同一条重连路径。
|
||||
func (a *Adapter) Connect(ctx context.Context) error {
|
||||
a.mu.Lock()
|
||||
if a.started {
|
||||
@@ -182,7 +188,7 @@ func (a *Adapter) Connect(ctx context.Context) error {
|
||||
case a.cfg.Transport == transportHTTP:
|
||||
err = a.connectHTTP(ctx)
|
||||
case a.cfg.Mode == modeClient:
|
||||
err = a.connectClient(ctx)
|
||||
err = a.connectFirstClient(ctx)
|
||||
default:
|
||||
err = a.connectServer(ctx)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user