FastAPI实战:WebSocket长连接保持与心跳机制,从入门到填坑 📅 发布时间:2026/7/13 10:09:48 👁️ 浏览次数: 摘要本文通过一个真实的上线案例详细讲解FastAPI与JavaScript实现WebSocket长连接保持的心跳机制。你会了解为什么连接会断、心跳原理是什么、前后端代码怎么写以及那些文档里没写的调优陷阱。照着做让你的实时通信稳如老狗。你是不是也遇到过——WebSocket连接动不动就断开尤其是在移动端用户切换个Wi-Fi或者电梯里信号晃一下消息就收不到了 用户投诉说“APP消息延迟”你一查日志满屏都是WebSocket disconnected然后疯狂重连服务器压力山大用户体验稀碎。有些项目图省事觉得WebSocket连上就行了结果线上跑了半天运维小哥就发来报警连接数忽高忽低很多连接存活不到2分钟。查日志好家伙Nginx默认proxy_read_timeout60秒加上移动网络运营商会掐掉长时间无流量的连接双向夹击连接全断了。核心结论WebSocket长连接保持不能靠“连上就不管”必须引入心跳机制——就像两个人打电话每隔一会儿问一句“喂还在吗”。今天我就把FastAPI后端 JavaScript前端的完整心跳实现掰开了揉碎了讲给你听顺便把我踩过的坑标红。 本文路线图 为什么WebSocket会断—— 中间件超时、网络状态变化 心跳原理ping-pong 还是 pong-ping FastAPI后端接收心跳消息 超时管理 JavaScript前端定时发送心跳 断线重连 完整可运行代码示例 那些年我踩过的坑间隔设置、重复定时器、服务端主动断开 第一部分连接为什么会断把WebSocket想象成一条水管数据就是水。如果水管一直流水它就不会堵。但要是你半天不放水中间的路由器、防火墙就觉得“嘿这管子是不是废弃了”——咔嚓一刀给你掐了。尤其是在移动网络下运营商的NAT网关空闲超时可能只有30秒到几分钟。还有我们常用的Nginx默认proxy_read_timeout是60秒一旦60秒内没有数据从后端发到客户端Nginx就会自作主张断开连接。所以要想让连接长存唯一的方法就是定期发送一些“无用”的数据告诉中间件“我还活着别砍我”——这就是心跳。 第二部分心跳机制的两种姿势心跳本质是一种ping/pong模式。WebSocket协议本身有控制帧Ping和Pong但浏览器原生JS的WebSocket API并没有直接暴露发送Ping帧的方法所以我们一般用普通消息模拟✨ 方案A客户端定时发送ping消息服务器收到后立即回复pong。✨ 方案B服务器定时发送ping客户端回复pong。但同样客户端需要能解析并回复。更常见的做法是客户端主动发心跳服务器只需响应或记录。为啥因为客户端更能感知网络变化且断开后能立即重连。下面我就以客户端发心跳为例上代码。⚙️ 第三部分FastAPI后端实战先搭一个最简单的FastAPI WebSocket端点。这里我用了/ws路径接收心跳消息约定JSON格式{type: ping}并回复{type: pong}。同时为了及时清理死连接我会记录每个连接的最后心跳时间启动一个后台任务检查超时比如60秒没收到心跳就主动close。/* by 01022.hk - online tools website : 01022.hk/zh/json2cs.html */ from fastapi import FastAPI, WebSocket, WebSocketDisconnect import asyncio import json from datetime import datetime, timedelta app FastAPI() class ConnectionManager: def __init__(self): self.active_connections: dict[WebSocket, datetime] {} self._heartbeat_check_interval 30 # 每30秒检查一次 asyncio.create_task(self.heartbeat_checker()) async def connect(self, websocket: WebSocket): await websocket.accept() self.active_connections[websocket] datetime.utcnow() print(f新连接加入当前连接数{len(self.active_connections)}) def disconnect(self, websocket: WebSocket): if websocket in self.active_connections: del self.active_connections[websocket] print(f连接断开当前连接数{len(self.active_connections)}) async def handle_messages(self, websocket: WebSocket): try: while True: data await websocket.receive_text() try: msg json.loads(data) except: continue # 如果是心跳ping更新最后心跳时间并回复pong if msg.get(type) ping: self.active_connections[websocket] datetime.utcnow() await websocket.send_text(json.dumps({type: pong})) else: # 其他业务消息按需处理 await websocket.send_text(json.dumps({type: echo, data: msg})) except WebSocketDisconnect: self.disconnect(websocket) async def heartbeat_checker(self): while True: await asyncio.sleep(self._heartbeat_check_interval) now datetime.utcnow() timeout timedelta(seconds70) # 超过70秒没心跳就断开 dead_conns [] for ws, last_ping in self.active_connections.items(): if now - last_ping timeout: dead_conns.append(ws) for ws in dead_conns: try: await ws.close(code1000, reasonheartbeat timeout) except: pass self.disconnect(ws) manager ConnectionManager() app.websocket(/ws) async def websocket_endpoint(websocket: WebSocket): await manager.connect(websocket) await manager.handle_messages(websocket) 重点说明-handle_messages里只处理心跳其他业务消息可以自定义。- 后台心跳检查线程每30秒跑一次如果某连接超过70秒没收到心跳就主动关闭。这个70秒一定要大于客户端的心跳间隔比如客户端30秒发一次那70秒大概漏掉2次都没回复才断防止网络抖动误杀。- 注意WebSocketDisconnect的捕获及时清理字典避免内存泄漏。 第四部分JavaScript前端实现前端主要做三件事建立连接、定时发心跳、监听断开自动重连。我习惯把WebSocket封装成一个类方便复用。直接上代码/* by 01022.hk - online tools website : 01022.hk/zh/json2cs.html */ class WebSocketClient { constructor(url) { this.url url; this.ws null; this.heartbeatInterval 30000; // 30秒一次心跳 this.reconnectInterval 3000; // 断线后3秒重连 this.heartbeatTimer null; this.reconnectTimer null; this.connect(); } connect() { this.ws new WebSocket(this.url); this.ws.onopen () { console.log(WebSocket 已连接); // 连接成功后启动心跳 this.startHeartbeat(); // 如果之前有重连定时器清掉 if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer null; } }; this.ws.onmessage (event) { const data JSON.parse(event.data); if (data.type pong) { console.log(收到心跳pong连接正常); // 可以在这里更新UI显示最后心跳时间但不必须 } else { // 处理其他业务消息 console.log(业务消息, data); } }; this.ws.onclose (e) { console.log(WebSocket 关闭, e.reason); // 停止心跳 this.stopHeartbeat(); // 尝试重连 this.reconnect(); }; this.ws.onerror (err) { console.error(WebSocket 错误, err); this.ws.close(); }; } startHeartbeat() { this.heartbeatTimer setInterval(() { if (this.ws this.ws.readyState WebSocket.OPEN) { this.ws.send(JSON.stringify({ type: ping })); console.log(发送心跳ping); } else { console.warn(连接未开启停止发送心跳); this.stopHeartbeat(); } }, this.heartbeatInterval); } stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer null; } } reconnect() { this.stopHeartbeat(); if (!this.reconnectTimer) { this.reconnectTimer setTimeout(() { console.log(尝试重连...); this.connect(); }, this.reconnectInterval); } } // 主动关闭连接比如页面卸载时 close() { this.stopHeartbeat(); if (this.ws) { this.ws.close(); } } } // 使用示例 const client new WebSocketClient(ws://你的域名/ws); // 页面关闭前主动清理 window.addEventListener(beforeunload, () client.close());⚠️ 关键细节- 心跳间隔不要超过Nginx的proxy_read_timeout一般设30秒比较安全。- 断线重连要防抖通过reconnectTimer避免重复重连。- 页面关闭时一定要close连接否则服务端可能保留孤儿连接直到超时。 第五部分跑起来看看效果启动FastAPIuvicorn main:app --reload打开浏览器控制台你会看到每隔30秒发送一次ping服务器立即回复pong。即使你断开Wi-Fi再打开客户端也会自动重连并且重连后心跳继续。 第六部分那些年我踩过的坑必看坑1心跳间隔太短服务器压力大—— 1秒一次纯属自残30秒一次足够既保活又省资源。坑2服务端没做超时主动断开—— 客户端突然掉线比如用户强制杀进程服务端不知道连接一直占着内存。所以后台心跳检查一定要有超时就close。坑3重连时忘记清理旧定时器—— 每次重连都新建一个setInterval导致多个心跳线程并发消息爆炸。解决方案重连前先stopHeartbeat()。坑4前后端心跳格式约定不一致—— 我用的是{type:ping}如果你后端用字段heartbeat一定记得对齐否则服务器不认相当于没心跳。坑5没考虑SSL/加密连接—— 生产环境用wss://证书配置要正确否则连接直接被拒绝。另外如果你想更优雅一点可以结合asyncio.timeout或者websocket.receive()的超时参数不过我觉得上面这种“记录最后心跳后台检查”的模式最清晰。 最后啰嗦一句心跳机制不是银弹但它确实是WebSocket长连接保持最简单有效的办法。结合断线重连能让你的实时应用在恶劣网络环境下依然坚挺。如果你在生产环境还有更高要求比如集群下的连接状态同步、心跳与业务消息优先级欢迎留言交流。老朋友提醒 这篇文章里的代码我都是用血泪教训换来的现在直接抄就能跑。但你的业务场景可能不一样比如心跳间隔是否需要动态调整服务端要不要主动ping欢迎在评论区分享你的“奇葩”踩坑经历或者收藏起来下次上线前翻出来看一眼也许能帮你省下一个通宵。如果你觉得有用点赞或分享给团队下次遇到WebSocket断连问题咱们就不再慌了。
软件即产品已成沉疴 上周我想要对一些图片进行抖动处理。这是一个非常非常具体的需求,我只是需要满足这个需求。(一种兔子洞式的注意力分散型需求) 有一个名为 Ditherboy 的商业应用可以很好地完成这个任务。它的售价为 45 英镑,可在 Mac 和 Windows… 2026/7/11 20:26:13
给你一张清单 9个降AI率平台测评对比 专科生必看 在当前学术写作中,AI生成内容的广泛应用让论文查重率和AIGC检测变得愈发重要。许多专科生在撰写毕业论文或课程作业时,常常因为使用AI工具而面临高AIGC率、被系统识别为AI生成的风险。这时候,一款高效、专业的AI降重工具就显得尤为重要。这些… 2026/5/17 5:37:42
亲测好用! AI论文网站 千笔 VS PaperRed,专科生写作更轻松! 随着人工智能技术的迅猛发展,AI辅助写作工具逐渐成为高校学生完成毕业论文的重要帮手。尤其是在专科生群体中,面对繁重的论文任务和紧迫的时间压力,越来越多的学生开始尝试借助AI工具提升写作效率。然而,市场上的AI写作工具种类繁… 2026/7/13 9:48:57
高压安全隔离技术:ISOM8710与STM32F722ZE的工业应用 1. 高压安全隔离的基础原理与行业需求在工业自动化、电力电子和医疗设备等领域,高压安全隔离是确保系统可靠性和人员安全的关键技术。传统的光耦隔离方案存在老化快、传输速率低等固有缺陷,而基于电容隔离的ISOM8710芯片则代表了新一代解决方案。电容隔离… 2026/7/13 10:08:41
Unity移动端AssetBundle下FairyGUI与Spine材质丢失问题分析与解决方案 1. 项目概述:当FairyGUI遇上Spine,移动端资源管理的“暗礁” 在Unity移动端项目的UI与动画开发中,FairyGUI和Spine的组合堪称“黄金搭档”。FairyGUI以其高效的UI编辑器和运行时性能著称,而Spine则是2D骨骼动画领域的行业标准&… 2026/7/13 10:08:41
C++凯撒密码实现:模运算在循环移位中的核心应用与洛谷P1914题解 1. 项目概述与核心价值最近在洛谷上刷题,看到P1914这道“小书童——凯撒密码”的题目,感觉特别有意思。它表面上是一个简单的字符串处理题,但背后却藏着循环移位这个在密码学和数据处理中非常核心的概念。很多刚接触C和算法的朋友,… 2026/7/13 10:04:39
软件工程可行性分析实战:3个维度量化评估,避免项目“烂尾” 软件工程可行性分析实战:3个维度量化评估,避免项目“烂尾” 去年参与的一个企业级SaaS项目让我深刻体会到可行性分析的价值。团队在立项阶段花费两周时间完成的技术可行性矩阵,后来成功预警了三个关键风险点,其中关于第三方API调用… 2026/7/13 10:04:39
OpenEuler sysBoost进阶指南:云计算环境中的5个最佳实践 OpenEuler sysBoost进阶指南:云计算环境中的5个最佳实践 【免费下载链接】Computing-docs Documentation Repository Dedicated to Computing Features 项目地址: https://gitcode.com/openeuler/Computing-docs 前往项目官网免费下载:https://ar… 2026/7/13 10:02:39
钢轨振动波传播特性分析工具:欧拉梁与Timoshenko梁双模型MATLAB脚本 本文还有配套的精品资源,点击获取 简介:一套面向铁路轨道振动研究的MATLAB计算工具,包含两个核心脚本:timo_guangyi.m基于Timoshenko梁理论,计入剪切变形和转动惯量,适用于高频、短波长下的横波与纵波色… 2026/7/13 10:02:39
HS2-HF Patch终极指南:如何用3步解决Honey Select 2的70+个痛点 HS2-HF Patch终极指南:如何用3步解决Honey Select 2的70个痛点 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch 你是否曾经在Honey Select 2中遇到过… 2026/7/13 0:01:19
语音转文字工具AsrTools:让音频整理变得简单高效 语音转文字工具AsrTools:让音频整理变得简单高效 【免费下载链接】AsrTools ✨ AsrTools: Smart Voice-to-Text Tool | Efficient Batch Processing | User-Friendly Interface | No GPU Required | Supports SRT/TXT Output | Turn your audio into accurate text … 2026/7/13 0:03:19
基于深度学习的蘑菇或花卉或动漫人物或中草药货水果蔬菜等识别系统31(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_ 基于深度学习的蘑菇或花卉或动漫人物或中草药货水果蔬菜等识别系统31(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_ 独家界面!不会重复,此项目属于本人原创,若有雷同,均是盗卖,各位买… 2026/7/13 0:05:20
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/13 8:31:55
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/13 9:31:08
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/13 2:34:55