为什么我的第一个 HTTP 请求因 WebSocket 行为而出现延迟,生产环境中如何处理?
发布: (2025年12月27日 GMT+8 22:32)
6 min read
原文: Dev.to
Source: Dev.to
请提供您想要翻译的具体文本内容,我将为您翻译成简体中文并保持原有的格式、Markdown 语法以及技术术语不变。谢谢!
问题描述
我正在构建一个 Web 应用,后端使用 FastAPI,前端在开发期间由 Live Server 提供服务。对某个端点的第一次 HTTP 请求明显比后续请求要慢。
观察
- 第二次请求 – 快速完成。
- 第一次请求 – 出现延迟。
- 当从 OpenAPI Docs(FastAPI 自动生成的文档)调用相同的端点时,响应会立即返回,即使是第一次调用也是如此。
- 在浏览器的 DevTools 中,我看到一个由 Live Server 为实时重载自动打开的 WebSocket 连接。该 WebSocket 似乎与常规 HTTP 请求竞争资源,导致初始延迟。
Questions
- 工程师在生产环境中如何处理需要同时使用 WebSocket 连接(例如实时通知)和普通 HTTP 请求的情况?
- 有哪些技术手段可以防止这两种连接相互干扰,即使是同一前端代码发起的?
- 如果 WebSocket 和 HTTP 流量都来自同一客户端代码,企业如何确保它们在真实环境中不会冲突?
- 我猜可能会使用 API 网关或不同的子域名,但我想要一个关于典型生产部署的简明解释。
生产就绪方案
1. 分离的主机名 / 子域名
- WebSocket 端点 在独立的主机名上暴露(例如
ws.example.com),而 REST/GraphQL API 位于api.example.com。 - 浏览器将它们视为独立的源,因此连接限制、TLS 会话和套接字池不会相互影响。
- DNS 和 TLS 证书可以统一管理(通配符或 SAN 证书),以保持部署简洁。
2. 专用负载均衡器 / 反向代理
- 七层负载均衡器(例如 NGINX、HAProxy、Envoy、AWS ALB)将
Upgrade: websocket请求路由到支持 WebSocket 的后端池,并将普通 HTTP 请求转发到另一个池。 - 负载均衡器为每种协议维护独立的连接池,防止资源争用。
3. 客户端的连接池隔离
- 现代浏览器已经为每个源和每种协议维护独立的连接池。
- 前端库(例如
fetch、axios、WebSocket)使用不同的底层套接字,只要源不同,它们就不会相互阻塞。 - 如果必须在同一源上同时提供两者,请确保服务器能够处理并发升级(大多数生产级服务器都支持)。
4. 使用 API 网关 / 服务网格
- API 网关(Kong、Apigee、AWS API Gateway)可以在统一域名下同时暴露 HTTP 和 WebSocket 路由,同时在内部将它们路由到不同的服务。
- 网关抽象了协议处理,使客户端只看到单一端点,而网关内部对流量进行隔离。
5. 扩展与资源分配
- 将 WebSocket 服务部署在 不同的一组实例(或容器)上,区别于无状态的 HTTP API。
- 这可以防止大量 WebSocket 连接耗尽用于 HTTP 请求处理的 CPU、内存或文件描述符限制。
6. CORS 与安全头
- 正确的 CORS 配置可确保浏览器允许同一前端源的两种连接,而不会产生不必要的预检延迟。
- 安全头(例如
Upgrade-Insecure-Requests)可以进行调优,以避免额外的往返请求。
为什么它在生产环境中有效
- 独立的源或路由规则 能确保浏览器的连接限制(通常每个主机最多 6 条并发连接)不会在 WebSocket 与 HTTP 流量之间共享。
- 生产服务器已配置为 高效处理
Upgrade头部,在建立 WebSocket 时不会阻塞处理普通 HTTP 请求的线程。 - 负载均衡器和网关保持 HTTP 与 WebSocket 服务的 事件循环 相互独立,从而长时间运行的 WebSocket 不会导致请求工作线程被饿死。
TL;DR
- 在生产环境中,WebSocket 和 HTTP 流量通常通过 主机名、反向代理路由或 API 网关 进行隔离。
- 这种隔离可以防止两种协议争夺同一连接池或服务器资源。
- 前端仍然可以从同一代码库发起两种连接;底层基础设施确保它们并行运行且互不干扰。