alireza0:3x-ui 连接池在 Xray 重启后受 gRPC backoff 影响恢复延迟
alireza0 在 3x-ui PR #6916 说明,复用 gRPC 连接后 Xray 重启会触发 grpc-go 重连退避;核心恢复约 50 毫秒后,连接池路径仍分别出现约 1.0 秒或 3.1 秒的失败窗口,而逐调用连接可立即成功。他建议在 Unavailable 时关闭并移除连接并补充重启回归测试。
作者原文@alireza0Thanks, pooling is the right direction. One behaviour change needs fixing first: a pooled connection recovers slowly after the core restarts, and a per-call one did not.
XrayAPI.Initusesgrpc.NewClientwith default connect params. Once the core goes away, the shared channel's reconnect attempts fail and grpc-go's connect backoff kicks in (1s base, ×1.6, up to 120s). For as long as that backoff lasts, every non-wait-for-ready RPC fails fast withUnavailable, even after the core is listening again.I reproduced it in
internal/web/runtimewith a fakeHandlerServicegRPC server on the API port, drivingLocal.withAPI(... DelInbound ...)through stop → restart:| scenario | this PR (pooled) | base (client per call) |
|---|---|---|
| core stopped, back after ~50 ms | first success 1.0 s after the core was back (10 failed calls) | first call succeeds |
| core down ~6 s with calls arriving, then back | first success 3.1 s after the core was back (31 failed calls) | first call succeeds |In the bulk paths each of those failures sets
needRestart, so a bulk op landing just after a core restart can trigger another restart for nothing.Suggested fix: keep the pooled client on the success path, but when an RPC fails with
codes.Unavailable, close and evict it so the next call dials fresh. That restores base behaviour on failure. Alternatively, tie the pool's lifetime to the core process instance rather than to the API port. Please add a regression test along the lines above: a fake handler server is stopped and restarted on the same port, and the first call after restart must succeed.