预计阅读 22 分钟
Web 性能优化 — 让你的网站快如闪电
用户的耐心极其有限——页面加载超过 3 秒,超过一半的用户会直接离开。Web 性能优化不仅是技术问题,更是直接影响用户体验和商业收入的关键因素。本章将从网络层面系统讲解提升 Web 性能的核心技术。
📋 开篇自测:你已经知道多少?
- CDN 是如何加速内容分发的?它的回源机制是怎样的?
- 四层负载均衡和七层负载均衡的区别是什么?各有什么优缺点?
- WebSocket 和 HTTP 长轮询有什么区别?WebSocket 的握手过程是怎样的?
一、CDN:把内容搬到用户身边
1.1 CDN 的工作原理
CDN(Content Delivery Network,内容分发网络)通过在全球各地部署边缘节点,将内容缓存到离用户最近的服务器上:
没有 CDN:
北京用户 -----(跨越2000公里)-----> 深圳源站
延迟: 50-100ms
使用 CDN:
北京用户 -----(几十公里)-----> 北京CDN边缘节点
延迟: 5-10ms
CDN 架构:
+-------------+
| 源站服务器 |
+------+------+
|
+----------+----------+
| CDN 中心节点 |
+----+----+----+-----+
| | |
+----+ +-+-+ +----+
| | | |
北京边缘 上海边缘 广州边缘 ...
| | |
用户A 用户B 用户C
1.2 CDN 的请求流程
步骤1: 用户请求 static.example.com/logo.png
步骤2: DNS 解析
用户 --> DNS --> CNAME 指向 CDN 的域名
CDN的智能DNS根据用户IP选择最近的边缘节点
返回边缘节点 IP: 1.2.3.4
步骤3: 用户请求到达边缘节点
步骤4: 缓存命中判断
├── 缓存命中(HIT): 直接返回缓存内容 (最快)
└── 缓存未命中(MISS): 回源获取
步骤5: 回源(如果需要)
边缘节点 --> CDN中心节点 --> 源站
获取内容后缓存到边缘节点
返回给用户
后续请求: 其他北京用户请求同一资源
--> 边缘节点缓存命中 --> 直接返回,无需回源
1.3 CDN 适合缓存什么
适合CDN缓存(静态内容): 不适合CDN缓存(动态内容):
├── 图片 (PNG, JPG, WebP) ├── API 响应(个性化数据)
├── CSS / JavaScript 文件 ├── 用户认证信息
├── 字体文件 ├── 实时数据(股票行情)
├── 视频 / 音频文件 └── 搜索结果
└── PDF / 文档
1.4 CDN 缓存策略
缓存控制头部:
Cache-Control: public, max-age=31536000 -- CDN缓存1年
加上文件指纹: /logo.abc123.png -- 内容变化时文件名变化
回源控制:
CDN边缘节点 --> If-None-Match: "etag" --> 源站
源站 304 Not Modified --> 边缘节点继续使用缓存
源站 200 新内容 --> 边缘节点更新缓存
缓存刷新:
通过 CDN 控制台或 API 主动清除边缘缓存
场景: 紧急修复错误的资源文件
🤔 想一想 如果你的网站在全国有用户,但源站只在深圳,不使用 CDN 时北方用户的体验会怎样?使用 CDN 后呢?
二、负载均衡:分散压力的艺术
2.1 为什么需要负载均衡
单台服务器的处理能力有限。当并发请求超过其承载极限时,就需要多台服务器分担负载:
没有负载均衡:
所有请求 -----> 单台服务器 (容易过载崩溃)
有负载均衡:
所有请求 -----> 负载均衡器 ----+---> 服务器1
+---> 服务器2
+---> 服务器3
均匀分配流量
2.2 四层负载均衡(L4 LB)
工作在传输层,根据 IP 地址和端口号分配流量,不理解 HTTP 内容:
四层负载均衡工作方式:
客户端 --> [src: 1.1.1.1:5000, dst: VIP:80]
|
v
L4 负载均衡器
(只看 IP + 端口)
|
+---------+---------+
| | |
后端1 后端2 后端3
转发方式:
- NAT 模式: 修改目的 IP 为后端服务器 IP
- DR 模式 (Direct Return): 不修改 IP,后端直接回复客户端
- IP 隧道模式: 使用 IP-in-IP 封装转发
代表产品: LVS (Linux Virtual Server), F5, 云厂商 NLB
2.3 七层负载均衡(L7 LB)
工作在应用层,能够解析 HTTP 内容,做出更精细的路由决策:
七层负载均衡工作方式:
客户端 --> GET /api/users HTTP/1.1
Host: api.example.com
|
v
L7 负载均衡器
(解析HTTP内容)
|
根据请求内容路由:
/api/* --> API 服务器集群
/static/* --> 静态资源服务器
/ws/* --> WebSocket 服务器
高级功能:
- 基于 URL 路径路由
- 基于 Host 头路由(多租户)
- 基于 Cookie/Header 路由(会话保持)
- SSL 终止(在 LB 处理 TLS)
- 请求重写和限流
代表产品: Nginx, HAProxy, Envoy, 云厂商 ALB
2.4 负载均衡算法
| 算法 | 原理 | 适用场景 |
|---|---|---|
| 轮询(Round Robin) | 依次分配给每台服务器 | 服务器配置相同 |
| 加权轮询 | 按权重比例分配 | 服务器配置不同 |
| 最少连接 | 分配给当前连接数最少的服务器 | 请求处理时间差异大 |
| IP 哈希 | 根据客户端 IP 计算哈希值 | 需要会话保持 |
| 一致性哈希 | 减少服务器增减时的重新分配 | 缓存场景 |
2.5 L4 vs L7 对比
+------------------+------------------+------------------+
| 特性 | 四层 (L4) | 七层 (L7) |
+------------------+------------------+------------------+
| 工作层次 | 传输层 | 应用层 |
| 理解HTTP | 否 | 是 |
| 性能 | 极高(百万级QPS) | 较高(十万级QPS) |
| 功能 | 简单转发 | 丰富的路由策略 |
| SSL终止 | 不支持/透传 | 支持 |
| 会话保持 | IP哈希 | Cookie/Header |
| URL路由 | 不支持 | 支持 |
| 典型场景 | 高并发TCP转发 | Web应用路由 |
+------------------+------------------+------------------+
三、连接优化
3.1 连接池
频繁创建和销毁 TCP 连接(包括 TLS 握手)开销很大。连接池预先建立并维护一组连接,供请求复用:
没有连接池:
请求1: TCP握手 + TLS握手 + 发送 + 接收 + 断开 (150ms开销)
请求2: TCP握手 + TLS握手 + 发送 + 接收 + 断开 (150ms开销)
请求3: TCP握手 + TLS握手 + 发送 + 接收 + 断开 (150ms开销)
使用连接池:
初始化: 预建3个连接 (一次性开销)
请求1: 从池中取连接 + 发送 + 接收 + 归还 (5ms开销)
请求2: 从池中取连接 + 发送 + 接收 + 归还 (5ms开销)
请求3: 从池中取连接 + 发送 + 接收 + 归还 (5ms开销)
连接池参数:
- 最小空闲连接数: 保持的最少连接(预热)
- 最大连接数: 不超过后端服务器的承载能力
- 空闲超时: 长时间未使用的连接回收
- 连接寿命: 定期更换连接,避免使用过期连接
3.2 HTTP Keep-Alive
HTTP/1.1 默认开启持久连接,在同一个 TCP 连接上串行发送多个请求:
Nginx 配置示例:
keepalive_timeout 65; # 空闲超时65秒
keepalive_requests 1000; # 单个连接最多处理1000个请求
上游连接池:
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
keepalive 32; # 每个worker保持32个空闲连接
}
3.3 TCP 优化参数
Linux 内核参数调优:
# 开启 TCP Fast Open (减少握手延迟)
net.ipv4.tcp_fastopen = 3
# 调整连接队列大小(防止 SYN Flood 丢连接)
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# TIME_WAIT 优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
# 窗口缩放(高带宽延迟场景)
net.ipv4.tcp_window_scaling = 1
# 拥塞控制算法
net.ipv4.tcp_congestion_control = bbr
四、WebSocket:全双工通信
4.1 HTTP 轮询的局限
传统 HTTP 是请求-响应模式,服务器不能主动推送数据。为了实现”实时”效果,早期方案是轮询:
短轮询(Short Polling):
客户端: 有新消息吗? --> 服务器: 没有
(1秒后)
客户端: 有新消息吗? --> 服务器: 没有
(1秒后)
客户端: 有新消息吗? --> 服务器: 有!这是新消息
问题: 大量无效请求,浪费带宽和服务器资源
长轮询(Long Polling):
客户端: 有新消息吗? --> 服务器: (不立即回复,挂起请求)
(等待30秒或有新消息)
服务器: 有新消息! --> 客户端: 收到! 立即发起新的长轮询
改进: 减少无效请求,但仍需频繁建立连接
4.2 WebSocket 的工作原理
WebSocket 在 HTTP 的基础上升级为全双工通信协议:
WebSocket 握手(基于 HTTP Upgrade):
客户端:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket <-- 请求升级协议
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbX...
Sec-WebSocket-Version: 13
服务器:
HTTP/1.1 101 Switching Protocols <-- 同意升级
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTx...
握手完成后:
客户端 <=====全双工WebSocket通道=====> 服务器
双方都可以随时主动发送消息
WebSocket vs HTTP 对比:
HTTP:
客户端 --> 请求 --> 服务器
客户端 <-- 响应 <-- 服务器
(每次通信都需要请求-响应)
WebSocket:
客户端 --> 消息 --> 服务器 (随时)
客户端 <-- 消息 <-- 服务器 (随时)
客户端 --> 消息 --> 服务器 (随时)
(建立连接后双方自由通信)
4.3 WebSocket 适用场景
适合 WebSocket: 不适合 WebSocket:
├── 即时通讯(聊天室) ├── 普通网页浏览
├── 实时数据推送(股票行情) ├── RESTful API 调用
├── 协同编辑(在线文档) ├── 文件上传下载
├── 在线游戏状态同步 └── 低频更新的数据
└── 实时通知系统
五、其他性能优化技术
5.1 数据压缩
HTTP 压缩:
客户端: Accept-Encoding: gzip, deflate, br
服务器: Content-Encoding: br (Brotli, Google开发, 典型场景下比gzip压缩率高20-25%)
压缩效果对比 (典型HTML文件):
原始: 100 KB
gzip: 25 KB (压缩率 75%)
brotli: 20 KB (压缩率 80%)
5.2 资源合并与拆分
HTTP/1.1 时代的优化:
- 合并小文件: 多个CSS/JS合并为一个(减少请求数)
- 雪碧图: 多个小图标合并为一张大图
- 内联小资源: 小图片转为 Base64 嵌入 HTML
HTTP/2 时代的变化:
- 多路复用使得合并不再必要
- 反而应该拆分: 独立文件利于缓存(只更新变化的文件)
- 雪碧图可以被单独图标替代
5.3 DNS 预解析与预连接
<!-- DNS 预解析: 提前解析第三方域名 -->
<link rel="dns-prefetch" href="//cdn.example.com">
<!-- 预连接: 提前建立 TCP + TLS 连接 -->
<link rel="preconnect" href="https://api.example.com">
<!-- 预加载: 提前下载关键资源 -->
<link rel="preload" href="/critical.css" as="style">
<!-- 预获取: 空闲时下载后续页面可能需要的资源 -->
<link rel="prefetch" href="/next-page.js">
5.4 性能指标与监控
核心 Web 指标 (Core Web Vitals):
LCP (Largest Contentful Paint) -- 最大内容绘制
衡量主要内容多快可见
目标: < 2.5 秒
INP (Interaction to Next Paint) -- 交互到下次绘制
衡量页面整体交互响应速度(2024年3月正式取代 FID)
目标: < 200 毫秒
CLS (Cumulative Layout Shift) -- 累积布局偏移
衡量视觉稳定性
目标: < 0.1
TTFB (Time to First Byte) -- 首字节时间
从请求到收到第一个字节的时间
目标: < 800 毫秒
受影响因素: DNS解析 + TCP握手 + TLS握手 + 服务器处理
🤔 想一想 一个电商网站首页加载慢,你会从哪些方面入手排查和优化?请列出至少 5 个可能的优化点。
六、章节小结
- CDN 通过在全球分布边缘节点缓存静态内容,大幅减少用户访问延迟。
- 负载均衡分为四层(基于 IP/端口)和七层(基于 HTTP 内容),各有适用场景。
- 连接池和 Keep-Alive 复用 TCP 连接,避免重复握手开销。
- WebSocket 提供全双工通信能力,适用于需要实时数据推送的场景。
- 数据压缩、资源优化、DNS 预解析等多种技术协同作用,提升整体 Web 性能。
📝 结尾自测:检验你的收获
- CDN 是如何通过 DNS 将用户请求引导到最近的边缘节点的?
- 四层负载均衡和七层负载均衡各自的优缺点是什么?什么场景下选择哪种?
- 连接池的核心参数有哪些?设置不当会导致什么问题?
- WebSocket 的握手过程是怎样的?它和 HTTP 长轮询相比有什么优势?
- 你知道 Core Web Vitals 包含哪些指标吗?每个指标衡量的是什么?
下一章预告:理解了网络协议和性能优化的原理之后,是时候动手写代码了。下一章我们将从最底层的 Socket 编程出发,一步步上升到 RESTful API 设计和 gRPC 高性能通信框架,把网络知识转化为实战能力。
购买课程解锁全部内容
网络通信第一课:10 章掌握计算机网络
¥29.90