当前位置:首页 > 文章列表 > 文章 > 前端 > WebSocket 怎样处理断线后的指数退避重连

WebSocket 怎样处理断线后的指数退避重连

来源:17golang原创 2026-11-08 04:27:15 0浏览 收藏

WebSocket 断线后,应由单一入口创建新连接,把连续失败次数转换为有上限的指数等待,并加入随机抖动;主动退出时同时取消定时器和关闭连接。浏览器原生 WebSocket 不会替应用自动完成这些恢复策略,不能在 error 和 close 里各启动一套重连。

标准入口:https://www.rfc-editor.org/rfc/rfc6455

Web API 文档:https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications

故障表现:恢复时连接请求反而变多

考虑一个教学故障场景:服务短暂不可用,一批页面几乎同时断线。第一次实现是在关闭事件里立即重新构造 WebSocket。服务尚未恢复,页面又失败;多个页面不断重复握手,反而增加恢复期间的负担。这里是机制推演,不是某次线上事故或实测数据。

再看局部代码的事件顺序:连接报错,错误回调安排一次重连;随后关闭回调又安排一次。表面上只是“多补一个兜底”,实际却可能留下两个计时器和多个连接实例。根因包含两个独立问题:失败后缺少等待,恢复任务没有唯一所有者。

把失败次数换成有上限的随机等待

RFC 6455 第 7.2.3 节讨论异常关闭恢复,建议先随机延迟,再逐步增加后续尝试的等待时间。工程上可使用截断指数退避:上限 C(n) = min(cap, base × 2^n),n 从第一次失败后的 0 开始。本文选 base 为 500 毫秒、cap 为 30 秒;这些是示例参数,不是协议硬性要求。

为避免所有页面按同一节奏重试,在 [C(n)/2, C(n)) 内随机取整数毫秒。采用这一半区间抖动时,第一次等待约 250–499 毫秒,第二次约 500–999 毫秒;到达上限后仍随机分散。其他抖动算法也可以使用,但前后端需要一致理解速率预算。

参数作用本例取舍
base初始等待尺度500 毫秒,可按服务负载提高
cap增长的最大上限30 秒,防止等待无限增长
jitter分散客户端尝试时间从上限的一半到上限随机选择
maxRetries限制自动恢复次数每个未稳定恢复周期最多 8 次
失败计数基础等待封顶参数随机抖动和重试预算的关系
静态结构图:失败计数、base 和 cap 共同定义等待上限,随机抖动定义实际等待区间,重试预算另行限制尝试数量;不是执行时间轴。

用一个重连入口管理连接和定时器

下面是浏览器端的连接管理骨架。一个管理器对应一个端点,内部维护一个 socket 引用、一个重连定时器和一个稳定性定时器。代码不依赖第三方库,省略了消息处理、鉴权刷新和连接建立超时,接入时需在业务层补充。

function createReconnector(url) {
  let socket = null;
  let retryTimer = null;
  let stableTimer = null;
  let attempts = 0;
  let stopped = false;
  const maxRetries = 8;

  function stop() {
    // 主动退出先禁止重连,再释放两个定时器和连接引用。
    stopped = true;
    clearTimeout(retryTimer);
    clearTimeout(stableTimer);
    retryTimer = stableTimer = null;
    const old = socket;
    socket = null;
    if (old) old.close(1000, "client stop");
  }

  function schedule() {
    // 每个管理器最多只有一个等待中的重连定时器。
    if (stopped || retryTimer !== null) return;
    if (attempts >= maxRetries) {
      stop();
      console.warn("自动重试已耗尽,请人工恢复");
      return;
    }
    const ceiling = Math.min(30_000, 500 * 2 ** attempts);
    attempts += 1;
    // 半区间抖动:等待在指数上限的一半与上限之间。
    const delay = Math.floor(ceiling / 2 + Math.random() * ceiling / 2);
    retryTimer = setTimeout(() => {
      retryTimer = null;
      connect();
    }, delay);
  }

  function connect() {
    if (stopped || socket !== null) return;
    let current;
    try {
      current = new WebSocket(url);
    } catch (error) {
      // URL 等同步配置错误直接停下,不进行无意义的快速重试。
      stopped = true;
      console.error("连接参数错误", error);
      return;
    }
    socket = current;

    current.addEventListener("open", () => {
      if (stopped || socket !== current) return;
      // 连续保持打开十秒才重置;这是示例策略,并非业务就绪证明。
      stableTimer = setTimeout(() => {
        stableTimer = null;
        if (!stopped && socket === current &&
            current.readyState === WebSocket.OPEN) attempts = 0;
      }, 10_000);
    });

    current.addEventListener("error", () => {
      // 网络错误还会触发 close,只在 close 中调度重连。
      if (!stopped && socket === current) console.warn("连接发生错误");
    });

    current.addEventListener("close", (event) => {
      // 旧连接的迟到事件不能控制新连接。
      if (socket !== current) return;
      socket = null;
      clearTimeout(stableTimer);
      stableTimer = null;
      if (stopped) return;
      // 示例约定:正常关闭和策略拒绝不自动重试。
      if (event.code === 1000 || event.code === 1008) {
        stopped = true;
        return;
      }
      schedule();
    });
  }

  connect();
  return { stop };
}
// 页面拥有者调用 createReconnector(自己的 wss 地址),卸载时调用 stop。
// 本示例只管理连接恢复,消息订阅与发送需在业务层另行接入。

error 只记录信息,close 统一决定是否重连。构造器同步抛出的参数错误直接停止,避免把配置问题当成瞬时网络故障。重试次数达到上限后,管理器进入停止状态;用户选择重试时,应在明确的页面操作中创建新的管理器。

端点地址应来自应用自身配置,HTTPS 页面通常使用 wss。不要把鉴权密钥写入日志,也不要向示例占位地址持续发起连接。正文代码是原创教学输入,没有声称已在某个浏览器环境执行。

避免刚连上就重置造成反复快重试

如果在每次 open 事件中立即把失败次数清零,服务“刚连上又断开”时,退避会一直退回最短等待。本例要求连接连续打开十秒才清零;在这十秒内关闭,稳定性定时器被取消,已有失败次数继续保留。

十秒仍只是粗略条件:打开连接不等于已完成业务鉴权,也不等于订阅已恢复。需要更强的恢复判据时,可在业务确认就绪后启动稳定性窗口。不能因为收到任意一条消息,就认定整个会话已经恢复。

连接管理器持有socket重连定时器稳定性定时器与停止标志的结构
静态说明图:管理器统一持有连接和两个定时器;stop 负责释放,连接身份判断隔离旧事件。连线表达所有权和约束,不表示事件先后。

防复发:关闭策略、生命周期与消息恢复

关闭码需要结合应用约定决定。本例不自动重试 1000 正常关闭与 1008 策略拒绝;其他错误也不应一概永久重试。服务自定义的鉴权失败、维护或限额关闭码,应进入相应的刷新凭据、等待或人工恢复路径,而不是反复撞同一个端点。1006 表示异常关闭的观测状态,不能把它当作客户端发出的关闭码。

页面或组件结束使用时调用 stop(),否则后台等待任务可能在组件卸载后创建新连接。页面隐藏不一定代表任务结束,可按产品需求保留或暂停;如果处理 pagehide/pageshow,应明确何时销毁旧管理器、何时建立新实例,避免路由层与页面层各自恢复一次。

重连成功后消息会自动补回来吗? 不会。需要服务端与客户端约定游标、订阅标识或快照恢复;重新发送操作还应处理幂等,不能把连接恢复当作消息恰好送达一次的保证。

定时器到期是否保证立刻发起连接? 不保证,后台标签页、设备休眠与事件循环都会影响触发时间。退避时间是应用请求的等待,不是精确时钟承诺。

网络恢复通知能否直接跳过退避? 可以作为重新评估的信号,但不能据此证明服务可达。若增加该入口,应复用同一个管理器并取消或协调旧定时器,不能再开一条并行重试链。

落地时关注连续失败次数、安排的等待区间、关闭码、当前连接标识及停止原因,并明确重试耗尽后的用户操作。单一所有者解决重复任务,指数等待与抖动控制尝试节奏,业务恢复协议负责补齐断线期间的数据缺口。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux 内核文档怎样定位某个 sysctl 参数Linux 内核文档怎样定位某个 sysctl 参数
上一篇
Linux 内核文档怎样定位某个 sysctl 参数
requestIdleCallback 怎样为非关键任务安排空闲执行
下一篇
requestIdleCallback 怎样为非关键任务安排空闲执行
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    935次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    939次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1048次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1025次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    850次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码