Skip to content

核心组件手册:LB、缓存、队列与 CDN

本文是系统设计系统学习系列的 L2 核心篇。前置:28. 系统设计四步分析法与估算。 学完可以配合面试题食用:23-cdn-design12-log-collection-system

为什么要专门讲这几个组件

写系统设计时,70% 的题会用到负载均衡、缓存、消息队列、CDN 这四个组件。它们不是"高级架构才有的待遇"——一个日活 10 万的系统照样可能用到。但问题在于:选错了比没有更糟。Nginx 做四层 LB 扛不住万级连接、Redis 热点 key 打崩整个集群、MQ 引入后光维护就比业务开发还累——这些坑不是因为组件不好,而是没搞清楚每个组件的边界在哪。

负载均衡:四层 vs 七层,分层部署

LB 的核心是"把流量分散到多个后端,不让任何一个扛不住"。但四层和七层干的活相差很大。

四层(L4):工作在传输层,只看 IP + 端口,不碰 HTTP 头。转发快(纯内核态,DPDK 可以做到 10M+ pps),但没法做基于 URL 的路由。典型:LVS(Linux Virtual Server)、云厂商的 SLB/NLB。

七层(L7):工作在应用层,能解析 HTTP 请求头、Cookie、路径做路由。灵活性高但吞吐低(Nginx 单机几万 QPS 就是极限了)。典型:Nginx、HAProxy、云 ALB。

实践中分层部署是标准做法:LVS(或云 SLB)做第一层,扛大流量再分给 Nginx 集群做七层转发。LVS 本身不处理 HTTP,只做四层转发,所以 CPU 开销极低。Nginx 既做反向代理也做 TLS 卸载。

调度算法要按场景选:

算法场景问题
轮询(RR)后端无状态,处理能力均等短连接场景可用,长连接会导致连接不均衡
最少连接请求处理时间差异大计算开销稍大,但更公平
一致性哈希需要会话保持/缓存亲和增删节点只影响相邻节点,但节点变化时 hash 环会抖动

健康检查是必须的配套。Nginx 的 ngx_http_upstream_module 支持主动探测:每隔几秒发一个请求,后端返回 5xx 就摘除,恢复后自动加回。

nginx
upstream backend {
    least_conn;
    server 10.0.1.1:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.2:8080 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

server {
    listen 80;
    location /api/ {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

keepalive 32 是容易被忽略的配置:让 Nginx 到后端的连接复用,否则每次请求都重建 TCP 连接,延迟从 1ms 变成 20ms+。

缓存层级:漏斗模型,每层拦多少

缓存不是"在 DB 前面加一层 Redis"就完事了。一个典型的 Web 请求要经过五层缓存,每层有各自的命中率和延迟:

浏览器缓存(10ms 内) → CDN(20-50ms) → 网关缓存(1ms) → 本地缓存(微秒) → Redis(毫秒) → DB(10ms+)

浏览器缓存:通过 Cache-Control: max-age=3600 告诉浏览器不要重复请求。静态资源(图片/CSS/JS)适合,动态接口不适用。

CDN:边缘节点缓存静态资源,动态内容加速(DSA)走优化过的回源链路。命中率 90%+ 就能把源站流量消掉 90%。

网关/反向代理缓存:Nginx 的 proxy_cache 可以缓存 GET 响应。适合读多写少的接口,但要注意缓存失效问题。

应用本地缓存:Guava Cache / Caffeine,毫秒级返回,适合热点 key 保护。常见做法:本地缓存兜热点,Redis 查不到再走本地,本地也没有才查 DB 回填。

Redis 分布式缓存:跨实例共享,但一次网络往返 0.1-1ms(同机房),比本地缓存慢 1000 倍。用来存"需要共享的热数据"。

各层之间有一个缓存穿透的陷阱:如果本地缓存和 Redis 都没有,所有请求都打 DB。解决方案:布隆过滤器挡不存在 key,或者缓存空值(短 TTL,30 秒内)。

消息队列:什么时候该引入,什么时候不该

MQ 解决三个问题:削峰、解耦、异步。但引入 MQ 也意味着你要维护一个分布式系统——它可能是全系统最复杂的组件。

什么时候该用

  • 流量波动大(秒杀瞬时流量是平均的 100 倍),需削峰填谷
  • 上游不关心下游结果(日志采集、通知推送),解耦为异步
  • 多个下游需要同一份数据(订单创建后触发库存扣减、积分发放、短信),用 MQ 广播

什么时候不该用

  • 请求必须同步返回结果(用户点支付要立刻知道成功还是失败)
  • 流量平稳且链路简单(加一个 MQ 只是增加延迟和运维复杂度)
  • 团队没人熟悉 MQ 运维(Kafka 的 ISR 抖动、分区再均衡,都够喝一壶的)

定量判断:假设峰值 QPS 5000,DB 扛 2000,MQ 引入后把流量削到 2000 以内。如果峰值本身就低于 DB 上限,MQ 对性能没帮助,反而增加 2-5ms 的延迟。

CDN 原理:DNS 调度与边缘缓存

CDN 做的不是"加速",而是缩短距离。用户在上海访问北京源站,网络 RTT 约 30ms;如果上海边缘节点有缓存,RTT 接近 0(本城 IDC 内)。

工作流程

  1. 用户请求 cdn.example.com/logo.png
  2. DNS 解析到 CDN 的 GSLB(全局负载均衡),GSLB 根据用户 IP 返回最近的边缘节点 IP
  3. 用户请求边缘节点,有缓存直接返回(命中),没有则回源站拉取(回源)
  4. 回源后边缘节点缓存 24 小时,下次同区域用户直接命中

动态内容加速(DSA):动态 API 不能缓存怎么办?CDN 通过优化回源 TCP 连接(预建连接、多路复用、最优路由)来减少延迟,而不是缓存响应体。

选型对照:日活 100 万以下的静态资源,用 OSS + CDN 的组合,OSS 做源站 CDN 做加速,成本可以控制在每月几百元。日活 1 亿以上要考虑自建 CDN 节点,但那是大厂的事。

组件选型决策卡:别拿大炮打蚊子

做系统设计最难的不是"选什么",而是"什么时候该选"。下面是按规模量化的决策参考:

规模负载均衡缓存消息队列CDN
日活 < 1 万Nginx 单机无需(DB 够用)不需要不需要
日活 1-10 万Nginx 主备本地缓存 + Redis 单机可选(日志场景)OSS + 云 CDN
日活 10-100 万LVS+Nginx 分层Redis 集群 + 本地缓存RabbitMQ / RocketMQ云 CDN 加速
日活 > 1000 万云 SLB + LVS 分层多级缓存 + 本地大内存Kafka 集群多厂商 CDN 容灾

边界值不是铁律,但方向一致:先确认瓶颈在哪,再选组件。DB 都没写优化就上 Redis,Redis 还没用明白就上 MQ——这是最常见的过度设计。

常见误区与小结

  • 四层 LB 比七层快,但不代表所有场景都用四层——需要 URL 路由时必须用七层
  • 加缓存不是"免费提速"——缓存一致性、穿透、雪崩、大 key 都是新问题,每解决一个就要花运维成本
  • MQ 引入就多了一个"可能丢消息"的环节——生产端、MQ 自身、消费端三处都要确认不丢才算可靠
  • CDN 配置了不等于生效——要配回源规则、缓存策略、预热策略,否则回源流量可能比不配还大
  • 组件不是为了"架构好看"而堆的——每加一个组件,故障面就多一个

小结:LB、缓存、MQ、CDN 是系统设计中最常用的四个积木。理解它们的原理和边界,比知道多高级的用法更重要。选型的关键是"量化"——知道当前规模在哪,就知道哪个组件该在哪一层出场。下一篇进入数据层设计:分库分表、NoSQL 与搜索引擎的选型实战。

参考

参考:NGINX 官方文档(Upstream 配置/健康检查/缓存),阿里云 CDN 最佳实践白皮书,Martin Kleppmann《Designing Data-Intensive Applications》第 1 章:可靠、可扩展与可维护的应用系统

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。
粤ICP备2026104257号-1