语音流重连陷阱:握手成功仍可能失效
Gladia 已结束的实时任务仍可完成 WebSocket 握手,随后以 4403 关闭;若在连接打开时重置退避,会导致客户端反复连接失效地址。文中通过稳定连接五秒后再重置退避、限频创建新任务,以及浏览器断开后清理上游连接来处理。
AI 深度解读
Gladia 的实时语音转文字(STT)连接有个容易误判的状态:任务已经结束,WebSocket 仍能完成握手,触发 open,随后以 4403 关闭。如果客户端把 open 当成重连成功,就可能不断拨打同一个失效地址。
作者给出的修复思路是:连接稳定存活五秒后才重置退避;任务结束后申请新地址;浏览器已经离开,就停止上游连接和任务创建。这篇短文值得看的,是它如何区分「握手成功」与「服务恢复」。
为什么重连一直失败,却始终等不到退避上限
重连通常会使用退避机制(backoff):连续失败时逐步延长等待时间,避免密集请求。问题出在,客户端拿什么事件判断「连续失败已经结束」。
原来的逻辑把 WebSocket 的 open 事件视为成功,随即将退避等待重置为零。但作者描述的 Gladia 行为是:已经结束的 live job 仍接受握手,连接打开后再以 4403 关闭。
于是客户端陷入循环:
- 使用原来的
wsUrl连接,握手完成。 open触发,退避被清零。- 连接以
4403关闭,客户端再次重连。 - 同一个地址再次接受握手,退避再次被清零。
等待时间没有机会累积,自然也到不了设定的上限。
open 只能证明握手成功,不能证明业务任务仍然可用。
作者将退避重置条件改为:连接持续存活五秒。刚握手就因 4403 关闭的连接,不再清除退避状态。这个改动解决的是重试节奏失控,失效地址本身还需要另一层处理。
任务结束后,该换地址还是继续重连
按原文对 Gladia 的描述,关闭码落在 4000 到 4999,表示 live job 已经结束。此时继续使用同一个 URL,只会重复握手流程,无法恢复原来的任务。
恢复路径是重新调用 POST /v2/live,取得新的 URL,再建立连接。作者同时给这项调用设置了限制:五分钟内最多两次。
这里有两个不同层次的控制:退避决定「隔多久再试」,新建任务的次数限制决定「最多创建多少次」。只控制前者,仍可能反复创建任务;只控制后者,也不能修正握手后立即断开的重连循环。
不过,原文没有说清「五分钟两次」是客户端策略还是服务端限制,也没有交代计数按用户、会话还是服务实例划分。接入时需要明确这个边界。
另外,4000 到 4999 的处理应限定在原文所述的 Gladia 场景,不能直接推广为所有 WebSocket 服务的通用语义。
浏览器离开后,服务端为什么也要停止恢复
连接恢复还有一个前提:结果仍然有人需要。
作者提到,如果浏览器已经断开,服务会关闭上游 socket,并跳过创建新的 live job。LiveSuggest 也会在尝试重连前执行相同检查。
这针对的是浏览器与上游服务之间存在中间服务的链路。页面离开后,中间服务若仍按原计划重连,就会继续维护已经失去消费者的连接,甚至创建新的任务。
因此,重试不应只问「连接失败了吗」,还应先问「当前会话还需要这个连接吗」。浏览器已经离开时,结束恢复流程才符合当前状态。
我的判断
我认同作者将握手成功、连接稳定、业务任务有效分开处理。相比「断开就重连、打开就清零」,这套判断更贴近实际业务,也解释了为什么单纯增加退避上限解决不了问题。
但这仍是一份故障修复经验,原文没有给出代码、日志、请求量变化或对照数据。五秒稳定窗口、五分钟最多两次,都只能视为作者采用的参数,不能直接当成通用最佳值。
五秒也只是稳定性的近似判断。连接活了五秒,未必已经能够正常转写。若服务提供明确的业务就绪信号,可以结合它判断恢复是否成功;原文没有说明 Gladia 在此处是否提供这类信号。
能拿来做什么
- 检查前端或 Node 客户端的重连逻辑,确认是否在
open回调里立即清零退避,并核对短暂连接反复关闭时等待时间能否继续增长。 - 接入 Gladia 时,将原文所述的任务结束关闭码单独处理,结束后通过
POST /v2/live获取新 URL,同时明确五分钟最多两次的计数范围。 - 如果 Node 服务代理浏览器与上游的连接,在重连和新建任务前检查浏览器是否仍在线;浏览器离开后,关闭上游 socket,跳过新任务创建。