HTTP的演进

HTTP的演进
DiffDay-
为何现代的 AI 对话产品,能实现流畅的「打字机」效果?
-
好像大家不再热衷谈论域名分片,文件打包等传输优化方案了
答案就在HTTP通信协议的演讲,继而洞察技术迭代背后的性能追求与架构思想。
HTTP 演进的动力:追求极致的 Web 性能
这是一部不断追求更快,更具交互性的历史。从静态文本到富媒体、单页应用、实时通信,传输协议势必也须跟上脚步。这又是一场与延迟与带宽限制的战争。
| HTTP1.0 1996 |
HTTP1.1 1999 |
HTTP2 2015 |
HTTP3 2022 |
|
|---|---|---|---|---|
| 核心问题 | 重建连接效率低,开销大 | 队头阻塞 | TCP 层面的队头阻塞 | UDP 部署挑战 CPU 开销 |
| 底层协议 | TCP | TCP | TCP | UDP(QUIC) |
| 连接模型 | 每次 TCP 握手连接 | 短连接/持久连接 | 单一 TCP 连接 | 单一 QUIC 连接 |
| 数据传输 | 文本格式 | 文本格式 | 二进制分帧 | 二进制分帧 |
| 并发 | N/A | 顺序请求 管道机制受代理服务器实现限制 |
多路复用 | 多路复用 |
| 头部压缩 | 无(重复发送大量文本) | 无(重复发送大量文本) | HPACK 动态字典和霍夫曼编码压缩 |
QHPACK |
| 安全性 | N/A |
HTTPS可选 | 不强制 但主流浏览器只支持 HTTPS |
强制加密 (TLS1.3 内嵌) |
| 连接建立 | TCP 三次握手 | TCP 三次握手+TLS 握手(2-3RTTs) | TCP 三次握手+TLS 握手(2-3RTTs) | 0-RTT/1-RTT 连接建立 |
| 连接迁移 | N/A | 不支持(IP/端口变动则连接中断) | 不支持(IP/端口变动则连接中断) | 支持(Connection ID),但部署仍面临 NAT 挑战 |
| 服务器推送 | N/A | 无(需 WebSocket 等) | Server Push(已被主流浏览器废弃) | 被废弃 |
| 流量控制 | 基于 TCP 滑动窗口 | 基于 TCP 滑动窗口 | 基于 Stream 和 Connection 的流量控制 | 基于 Stream 和 Connection 的流量控制 |
当时 HTTPS 为 Netscape基于 HTTP1.0+SSL 开发,但在 HTTP1.0 时代还不是标准的一部分,是一种扩展实现,标准支持在 1.1 时代正式确立。
HTTP1.1的变通智慧 VS HTTP2的反模式
通常浏览器对同一个域名限制 6 个 TCP 连接,所以大家就研发了一些妥协变通的前端黑科技,目的聚焦于突破并发限制和减少 HTTP 请求数,这些在新时代又变成反模式
| HTTP1.1 | HTTP2 | |
|---|---|---|
| 域名分片 | 多个子域名,突破浏览器限制 | 徒增加额外的 DNS 查询和 TCP 连接开销 |
| 文件打包 | 多个 css 和 js 文件合并成一个,减少 HTTP 请求数 | 精细的代码分割更有利 |
| 雪碧图(CSS Sprites) | 多个小图标合成一张大图,通过background-position 来显示,减少图片请求 | 同上 |
| 内联 | 将小 CSS 和 JS或图片(Base64)直接嵌入 HTML 中,减少 HTTP 请求数 | 同上 |
HTTP2 时代,优化策略从请求合并转向了按需、精细化地加载。由于底层协议解决了并发传输的瓶颈,使得上层应用可灵活组织和交付资源,拿到更好的缓存效率和首屏性能。
一个巨大的文件,哪怕只改了一行代码,用户也需重新下载整个文件,无法利用浏览器缓存。一个大 JS文件的任何阻塞,都会影响整个应用的渲染。
例外:弱网或高丢包率环境下,TCP 的队头阻塞可能被放大,这时HTTP1.1 的多域名优化策略仍然凑效。
HTTP2虽先进,但优势是建立在相对稳定的网络上的,所以要关注用户监控数据,判断客户端网络质量,动态决定是否回退 HTTP1.1 的多连接模式。
HTTP3-拥抱 UDP与挑战
QUIC(Quick UDP Internet Connections)基于 UDP在用户空间实现高效、可靠、安全的传输。但其用户空间的实现也意味着不如内核态高效,会更多的消耗 CPU 资源。
但部署有其挑战,来自全链路设备生态,老旧防火墙可能限制 UCP 流量,这时需回退至HTTP2 或 HTTP1.1。大型 CDN 服务是启用 HTTP3 的最现实和最高效方式,它们处理了协议协商,回退策略和全球网络优化等复杂问题,比自行部署优化更省力。
Web 通信的模式演进
- 请求-响应模型
- 轮询:客户端为获取实时数据,不停问,有新数据么…,效率低,浪费资源
- 长轮询:客户端问一次,服务端直到有数据再回答。减少了无效请求,但连接管理复杂
- WebSocket:全双工 TCP 连接,功能强大但协议复杂,还需自设计,有时会遇到网络设备(代理、防火墙)阻碍
如果我只需要服务端向客户端单向推送数据,不想 WebSocket 这么复杂,有没有办法?
-
SSE:一个不断开的 HTTP 连接,服务端响应 Content-Type:text/event-stream,告知浏览器接下来的不是一个完整的数据块,请按事件流方式解析它。每个事件由若干文本行组成,以两个换行符(br/)结束。数据流由严格又简单的文本格式构成。
data:<message>是事件的数据内容一次可以有多行data,它们会被拼接起来event:<event_name>(可选) 事件的类型。如果没有指定,客户端会监听到默认的message事件,客户端也可以监听不同类型的事件id:<unique_id>(可选) 事件的唯一ID。如果连接中断,浏览器会自动重新连接,并通过 HTTP HeaderLast-Event-ID将这个 ID 发送给服务器,方便服务器从上次中断的地方继续发送。这是 SSE 自带断线重连机制的关键retry:<milliseconds>(可选) 告诉浏览器在断线后应该等待多少毫秒再尝试重连
-
Streamable HTTP:利用 HTTP/1.1 中的
Transfer-Encoding:chunked特性及 HTTP2 的原生流支持。相较于 SSE 只能是固定格式的文本,Streamable HTTP可传任意数据(JSON,HTML 片段或二进制数据),并由前端自定义解析逻辑。分块格式:当前 chunk 大小(十六进制表示),后跟
\r<br />; chunk 的数据内容,后跟\r<br />。最后一个数据,会发送一个大小为 0 的块,表示结束0\r<br />\r<br />
| WebSocket | SSE-更上层 | Streamable HTTP-更底层通用 | |
|---|---|---|---|
| 特征 | 全双工,应用协议(ws/wss)独立于 HTTP,仅握手阶段依赖 HTTP ,用于高频双向通信 |
自带事件机制、自动重连、专注推送事件场景 | 给你原始数据块(Unit8Array),你拥有完全控制权 可解析任何自定义流式格式 |
| 断线重连 | 需手动实现(包括心跳保活) | 原生支持(Last-Event-ID) | 需手动实现 |
| 兼容性 | 可能会被旧防火墙阻挡 | 就是 HTTP | 就是 HTTP |
| 适用场景 | 聊天室、在线游戏、协同编辑(在线画板) | 实时通知、状态更新、日志流 | 大文件流式渲染、AI 对话、自定义协议 |
需注意中间网络设备的超时调整和负载均衡粘性设定!
AI-MCP世代
MCP以 SSE 为基础,在早期版本实现了一种混合模式,解决双向通信问题,避免引入更复杂双向协议的开销。
SSE开发成本低,流式交互友好,长连接与自动重连开箱即用
- 客户端先通过 SSE 订阅服务端推送(生成式长文本,如代码生成、多步推理过程展示)
- 服务端会指示客户端通过一个专用 HTTP端点(如/message) 发送后续消息
SSE不足
- Last-Event-ID 无法支撑复杂的用户态会话状态
- 服务器维持 SSE 长连接的开销和故障迁移问题
- 简单的请求,也必须以 SSE 返回消息,徒增负担
- 负载均衡和网关可能未对长效 SSE 做兼容处理
Streamable HTTP
- 统一端点,移除专用的/sse 端点,所有通信都通过单个端点进行(如/message),简化协议层设计和客户端配置
- 普通 HTTP 响应和 SSE 流均灵活由服务端支持,友好于简单云函数
- 会话标识与无状态服务支持
- 引入 Mcp-Session-Id,识别统一会话的多次交互
- 方便服务端可设计成无状态的
##参考













