终于跨过 TCP,我们来到 发送http请求,响应阶段
TTFB
所谓 TTFB 就是 responseStart- requestStart
一、TTFB的“真面目”与技术本质
graph TD
A[浏览器发起请求] --> B[服务器接收]
B --> C[业务逻辑处理]
C --> D[响应生成]
D --> E[网络传输]
E --> F[浏览器接收首字节] --> G[TTFB结束点]
- TTFB真实包含:
网络延迟与真实业务处理耗时- 网络往返延迟(RTT)
- 服务器队列等待时间
- 后端处理时间(Server RT)
- 首字节网络传输时间(通常极小)
如何优化TTFB
既然 TTFB真实包含:网络延迟与真实业务处理耗时,那么业务耗时主要在服务器中优化,对于前端可以做的就是 网络延迟优化
如何优化网络往返延迟(RTT)-- 尽量减少请求头和请求体
示例:一个登录请求,通常需要发送用户名和密码,但有时前端可能会附带很多额外的元数据(如设备信息、跟踪信息等)。如果这些信息不是登录所必需的,应该移除或通过其他方式发送(比如在登录成功后发送)。
技术细节:
- 在HTTP/1.1中,每个请求都需要完整发送头信息,且头信息不压缩(HPACK仅在HTTP/2中引入)。
- 即使使用HTTP/2,头信息使用HPACK压缩,但过大的头仍然会带来传输负担。
- 对于请求体,使用GZIP等压缩方式可以减小体积,但压缩和解压缩都需要时间,而且对于已经压缩的数据(如图片)效果不大。
优化策略:
-
减少Cookie大小:
- 避免在Cookie中存储不必要的数据。
- 使用服务器端会话存储(Session Storage),只在Cookie中存储一个会话ID。
- 设置合理的Cookie过期时间。
-
减少POST请求体大小:
- 移除不必要的字段。
- 对数据进行压缩(如JSON数据使用GZIP压缩,但注意:客户端需要设置Content-Encoding,并且服务器需要支持解压缩)。
- 对于大文件上传,使用分片上传(multipart upload)或断点续传,但这通常是为了避免单次请求超时,而不是为了减小单次请求大小。
-
使用HTTP/2:
- HTTP/2的多路复用和头部压缩可以减轻头部负担,但请求体过大的问题仍然存在。
-
考虑使用查询参数代替请求体(仅适用于GET):
- 对于GET请求,数据通过URL参数传递。注意URL长度限制(不同浏览器限制不同,一般为2KB-8KB),而且URL参数在日志中容易被记录,不适合敏感数据。
实际测量: 使用浏览器开发者工具或Wireshark,可以观察到:
- 当请求头或体很大时,在发送请求阶段(Request sent阶段)会占用较长时间。
- 在WebPageTest的瀑布图中,你会看到发送请求(Request)的横条很长。
结论: 为了优化性能,特别是减少TTFB,应该尽量减小请求的大小,特别是避免在请求头中携带过大的Cookie,以及在POST请求中发送过大的请求体。
如何优化Response 阶段性能
传输速度和压缩速度如何兼得
一、内容压缩原理:客户端与服务端的协商机制
1. 关键 Header 的作用
| Header | 位置 | 功能 |
|---|---|---|
Accept-Encoding | 请求头 (Request) | 客户端声明支持的压缩算法(如 gzip, deflate, br),服务端从中选择。 |
Content-Encoding | 响应头 (Response) | 服务端声明实际使用的压缩算法(如 gzip),客户端据此解压。 |
2. 工作流程(图 11-2)
- 客户端请求:携带
Accept-Encoding: gzip, br - 服务端响应:
- 选择一种客户端支持的算法(如
gzip)压缩内容。 - 返回响应头
Content-Encoding: gzip+ 压缩后的数据。
- 选择一种客户端支持的算法(如
- 客户端解压:根据
Content-Encoding用对应算法解压数据。
✅ 价值:减少网络传输量(通常压缩后体积减少 60%~80%),显著提升加载速度。
二、主流压缩算法对比
| 算法 | 压缩率 | 压缩速度 | 解压速度 | 适用场景 |
|---|---|---|---|---|
| Gzip | 中等 | ⚡️ 快 | ⚡️ 快 | 通用文本(HTML/CSS/JS) |
| Brotli | 高 | ⚠️ 慢 | ⚡️ 快 | 静态资源(高压缩率优先场景) |
| Deflate | 低 | ⚡️ 快 | ⚡️ 快 | 历史遗留系统(已逐渐淘汰) |
- 关键结论:
- Brotli(br) 压缩率更高(比 Gzip 高 15%~20%),但压缩耗时显著更长(图 11-4)。
- Gzip 在压缩速度与压缩率间取得平衡,适合实时压缩。
三、压缩策略:实时压缩 vs 预压缩
1. 实时压缩(图 11-5)
- 工作原理:
服务端(如 Nginx)在响应请求时动态压缩内容(如配置gzip on;)。 - 优点:
- 灵活处理动态内容(如 API 响应)。
- 节省存储空间(无需存储压缩副本)。
- 缺点:
- 增加 CPU 开销:每次请求需实时压缩。
- 增加响应延迟:压缩耗时计入 TTFB(首字节时间)。
2. 预压缩
- 工作原理:
提前将静态文件(如app.js)压缩为app.js.gz/app.js.br,服务端直接发送预压缩文件。 - 优点:
- 零压缩延迟:直接读取已压缩文件,TTFB 更低。
- 降低 CPU 负载:避免重复压缩。
- 缺点:
- 需额外存储空间(保留原始文件与压缩文件)。
- 仅适用于静态资源。
3. 性能权衡
“传输时间节约 > 压缩耗时 + 解压耗时”
- 典型场景:
- 1 MB 文本文件 → Gzip 压缩后 200 KB。
- 压缩耗时 50ms + 解压耗时 20ms = 总成本 70ms。
- 传输节约:800 KB(假设 10 Mbps 带宽 → 节省 640ms)。
- 净收益:570ms(网络节省远大于压缩开销)。
- 例外:
高速网络(如 5G)下小文件( 10 KB)可能得不偿失。
四、静态资源压缩的进阶策略:从实时压缩到离线压缩
1. 实时压缩的瓶颈
- 问题:
对大文件(如 1MB+ 的 JS/CSS)使用 Brotli(br)实时压缩时:- 压缩耗时高(图 11-4 中 br 压缩时间 ≈ Gzip 的 3-5 倍)。
- 总耗时可能增加:压缩时间 > 传输节省时间(尤其在低延迟网络中)。
- 例:
- 未压缩文件:传输 2 MB(耗时 1600ms @ 10 Mbps)。
- br 实时压缩:压缩耗时 300ms → 压缩后 400 KB → 传输 320ms。
- 总耗时:300ms(压缩)+ 320ms(传输) = 620ms(反而高于未压缩方案)。
2. 离线压缩(预压缩)的解决方案
- 原理:
- 在代码构建阶段(如 Webpack)提前生成压缩文件(如
app.js.br,style.css.gz)。 - 服务端直接托管这些文件,省去实时压缩开销。
- 在代码构建阶段(如 Webpack)提前生成压缩文件(如
- 工作流程:
- 客户端请求:
Accept-Encoding: gzip, br - 服务端检查:
- 若存在
app.js.br→ 返回Content-Encoding: br+ 预压缩文件。 - 若存在
app.js.gz→ 返回Content-Encoding: gzip+ 预压缩文件。
- 若存在
- 零压缩延迟:TTFB(首字节时间)显著降低。
- 客户端请求:
✅ 优势:
- 解决 Brotli 压缩慢的问题,发挥其高压缩率优势(比 Gzip 小 15%~25%)。
- 降低服务端 CPU 压力,提升并发能力。
五、压缩的禁区:媒体文件
“图片/视频无需额外压缩”
- 原因:
- 图片(JPEG/PNG/WebP)、视频(MP4/WebM)本身是有损压缩格式。
- 二次压缩(如 Gzip)几乎不减小体积,反而浪费 CPU。
- 检测方法:
- 在 DevTools 的 Network 面板查看资源大小:
- 若
Size(传输大小) ≈Content-Length(原始大小)→ 压缩无效。
- 若
- 在 DevTools 的 Network 面板查看资源大小: