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
@@ -114,20 +114,26 @@ func (a *Adapter) Addr() string {
|
||||
}
|
||||
|
||||
// readLoop is the single inbound-reader goroutine. It reads frames from the
|
||||
// active connection, dispatching message events; in client mode it reconnects
|
||||
// after drops, in server mode it waits for a new OneBot client.
|
||||
// active connection, dispatching message events; in client mode it connects and
|
||||
// reconnects after drops (exponential backoff, see reconnect.go), in server mode
|
||||
// it waits for a new OneBot client.
|
||||
func (a *Adapter) readLoop(ctx context.Context) {
|
||||
for {
|
||||
conn := a.currentConn()
|
||||
if conn == nil {
|
||||
if a.cfg.Mode == "client" {
|
||||
if a.cfg.Mode == modeClient {
|
||||
// ws+client:首连失败与断线重连**共用这一条路径**——Connect 首连失败
|
||||
// 只是记一条 WARN 就返回(不致命),真正的重试在这里按退避进行。
|
||||
// 先等待、再拨号:日志里承诺的 retryIn 就是这次等待。等待结束后再查一次
|
||||
// ctx:与 Disconnect 赛跑时宁可少拨一次,也不发起注定失败的拨号。
|
||||
if !sleepCtx(ctx, a.reconnect.wait(a.policy)) || ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if err := a.connectClient(ctx); err != nil {
|
||||
a.log.Warn("onebot reconnect failed", "err", err)
|
||||
if !sleepCtx(ctx, 3*time.Second) {
|
||||
return
|
||||
}
|
||||
a.noteConnectFailure(err)
|
||||
continue
|
||||
}
|
||||
a.reconnect.reset()
|
||||
} else if !a.waitConn(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user