2019年双十一,天猫订单创建峰值达到 54.4万笔/秒 。 2020年双十一,订单峰值 58.3万笔/秒 。 2021年,每秒 4982亿次实时计算...
这些数字什么概念?
想象一下:全国人民同时点外卖,美团服务器大概会这样:
yaml
你 ──→ 外卖服务器
↓
服务器:💥 这谁顶得住啊!
↓
宕机...全员崩溃...
但双十一从来没有宕机过。为什么?
今天聊聊,负载均衡是怎么让"一个人扛"变成"一堆人扛"的。
原文地址
一个厨师干不了的事
单机天花板
假设你开了一家小餐厅,只有一个厨师:
yaml
顾客1 → 厨师(做菜)→ 上菜
顾客2 → 厨师(做菜)→ 上菜 (等...)
顾客3 → 厨师(做菜)→ 上菜 (等...)
...
顾客100 → 厨师(做菜)→ 上菜 (等到天荒地老...)
一个厨师同时只能做一道菜。顾客多了就要排队,排队久了就走人。
服务器也是一样的道理。
一台服务器的 CPU、内存、带宽都是有限的。当请求量超过服务器的"精力",就会出现:
| 问题 | 表现 |
|---|---|
| 响应变慢 | 用户等半天没反应 |
| 超时 | 请求等太久,直接失败 |
| 宕机 | 服务器扛不住,直接崩了 |
加厨师!谁来分配?
加10个厨师就行了:
yaml
顾客1 → 厨师A → 上菜
顾客2 → 厨师B → 上菜
顾客3 → 厨师C → 上菜
...
但新问题来了:顾客怎么知道该找哪个厨师?
这时候就需要一个迎宾员。
负载均衡:谁来当迎宾员?
什么是负载均衡?
负载均衡 (Load Balancer)= 把请求均匀分配到多台服务器。
就像餐厅迎宾员:顾客进门,迎宾员安排到哪个厨师。
yaml
请求 ──→ 迎宾员 ──→ 服务器A
──→ 服务器B
──→ 服务器C
三种分配策略
| 策略 | 原理 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|---|
| 轮询 | 挨个分配 | 简单、公平 | 不管服务器性能差异 | 服务器配置相同的场景 |
| 最少连接 | 谁闲谁上 | 动态平衡 | 连接数≠实际负载 | 各请求耗时差异大的场景 |
| IP哈希 | 固定IP打固定服务器 | 会话保持 | 服务器缩容会丢会话 | 需要保持用户状态的场景 |
怎么选?
- 服务器配置一样 → 轮询
- 请求耗时差异大 → 最少连接
- 需要会话保持 → IP哈希
举例:
yaml
# 轮询:简单粗暴,挨个来
请求1 → 服务器A
请求2 → 服务器B
请求3 → 服务器C
# 最少连接:谁闲谁上
服务器A(5个连接)
服务器B(2个连接)← 选这个
服务器C(8个连接)
# IP哈希:同一IP永远打同一台
某个IP的请求 ──→ 哈希计算 ──→ 固定某台服务器
分配策略定了,但负载均衡在哪一层做?
请求进来之后,在不同网络层级做分配,能力是不同的:
L4 和 L7 的区别
| 层级 | 工作在 | 特点 | 例子 |
|---|---|---|---|
| L4 | IP + 端口 | 快,不解析内容 | LVS、F5 |
| L7 | HTTP/HTTPS | 懂业务,按URL/Cookie分配 | Nginx、Envoy |
yaml
L7 负载均衡:看得懂 URL
/api/users → 用户服务
/api/products → 商品服务
L4 负载均衡:只看 IP + 端口
192.168.1.1:80 → 服务器A
192.168.1.2:80 → 服务器B
实际用法:L4 做入口分发,L7 做业务路由。
负载均衡搞定了,但如果厨房要"换厨师"怎么办?
流量分配、高可用都搞定了。但如果厨房要换新厨师(更新代码),不能直接换------万一新厨师出问题,整个餐厅就乱了。
金丝雀发布:怎么安全地更新版本?
想象餐厅换新菜单
你要换一批新菜品,直接让所有客人都吃,万一出问题就全砸了招牌。
更好的做法:先印10份新菜单,给几桌客人试试,好用再全面推广。
这就是金丝雀发布。"金丝雀"是最先尝鲜的少数用户。
怎么发布?
yaml
旧版本A(老菜单)← 90%客人
新版本B(新菜单)← 10%客人
观察:
- 新菜单没问题 → 逐步加大比例
- 新菜单有问题 → 立刻换回老菜单
核心:先让少量用户试水,没问题再全量上线。
yaml
第一阶段:10% 流量到新版本
↓ 观察错误率
第二阶段:50% 流量到新版本
↓ 观察错误率
第三阶段:100% 流量到新版本
金丝雀 vs 蓝绿部署
| 方式 | 原理 | 资源 | 切换速度 |
|---|---|---|---|
| 金丝雀 | 新旧版本共存,逐步切换 | 少 | 慢 |
| 蓝绿 | 两套完整环境,一键切换 | 多 | 快 |
蓝绿部署就是:准备好一套全新的,等它测试通过后,DNS 一刀切换过去。回滚也是一刀切回来。
换菜单的问题解决了,但迎宾员自己也有问题------万一迎宾员累趴了呢?
高可用:迎宾员挂了怎么办?
只有一个迎宾员的问题
yaml
迎宾员A ← 单点故障!
↓
所有顾客不知道往哪走
解决方案:再加一个迎宾员。
yaml
主迎宾员A ←→ 心跳检测 ←→ 备迎宾员B
A活着:B 闲着
A挂了:B 马上顶上
这就是**高可用(HA)**的核心:冗余备份。
熔断:服务器"跳闸"保护
想象一下:厨房着火冒烟了,迎宾员还一个劲往里塞客人,结果整个餐厅都完了。
熔断就是当某个服务出问题时报错"别塞了,等一下":
yaml
服务A(正常)→ 继续接收请求
服务A(报错增多)→ 触发熔断
服务A(熔断中)→ 直接返回"服务繁忙",不往里塞
等服务A恢复后 → 逐步放行请求
熔断 vs 限流:
- 限流:人多时"排队取号"
- 熔断:厨房着火时"暂停营业"
健康检查:怎么知道厨师还活着?
迎宾员要时不时看看后厨:
yaml
迎宾员:嘿,师傅,还在吗?
厨师A:活着!(2ms)
厨师B:...(没响应)
厨师C:活着!(3ms)
→ 把厨师B踢出队列
→ 等恢复了再加回来
核心就三点:
- 定时探测
- 失败就标记"不健康"
- 不再往这台机器分配请求
高可用搞定了,但还有一个问题:用户登录后,下次请求换到另一台服务器,还认识他吗?
登录状态:换台服务器怎么还认识我?
你登录了淘宝,刷新页面还是登录状态。但如果负载均衡把你从服务器A换到了服务器B...
yaml
服务器A(存着登录状态)
服务器B(没有登录状态)
你的请求被换到服务器B → 要重新登录!
三种解决方案:
| 方案 | 原理 | 缺点 |
|---|---|---|
| Session同步 | 所有服务器都存一份 | 同步慢,占内存 |
| Session绑定 | 同一IP永远打同一台 | 服务器挂了状态丢 |
| Session外置 | 存Redis,所有服务器共享 | 依赖Redis,Redis挂了也完蛋 |
yaml
用户请求 ──→ 服务器A ──→ Redis(Session)
↓
所有服务器都能访问
Session问题解决了,接下来看看更前一层------DNS怎么帮忙分流。
DNS轮询:最简单的负载均衡
DNS = 域名解析。把 www.taobao.com 变成 IP 地址。
普通DNS:一个域名 → 一个IP
DNS轮询:一个域名 → 多个IP,轮流返回。
yaml
用户1 → DNS → IP1(服务器A)
用户2 → DNS → IP2(服务器B)
用户3 → DNS → IP3(服务器C)
...
优点:简单
缺点:
- DNS 不知道服务器挂了(没有健康检查)
- DNS 不知道哪台忙(没有智能分配)
DNS轮询适合做"第一层分流",配合 L4/L7 负载均衡使用。
分层搞清楚了,下面用一张图串起来。
一张图:流量是怎么走的
yaml
用户请求
↓
DNS轮询(宏观分流)
↓
L4/L7 负载均衡(智能分配)
↓
应用集群(真正干活)
↓
Redis(Session共享)
↓
数据库(持久化)
| 层级 | 负责什么 |
|---|---|
| DNS轮询 | 高速路口分流 |
| L4/L7负载均衡 | 商场迎宾员 |
| 应用服务器 | 厨师做菜 |
| Redis | 记忆 |
| 数据库 | 保险箱 |
理论讲完了,来个实战。
实战:秒杀系统怎么设计?
需求: 100万人同时下单,每秒处理10万订单,不能宕机。
三招:
1. 分 --- 多层分流
yaml
用户端限流(token验证码)
↓
Nginx 负载均衡
↓
网关集群(限流熔断)
↓
应用集群(库存/订单/支付服务拆分)
↓
消息队列(Kafka)削峰
2. 保 --- 每一层都备份
没有单点,任何一台挂了都能切换。
3. 限 --- 超过能力直接拒绝
超过阈值的请求直接返回"抢光了",保护系统不被压垮。
以上就是双十一不宕机的秘密。
写在最后
双十一从不宕机,不是因为服务器特别牛,而是架构设计得好。
本质就三招:
yaml
分 ------ 流量分散到多台机器
保 ------ 备份、心跳、故障转移
限 ------ 超过能力的直接拒绝
下次双十一剁手的时候,可以想想:这么顺畅的体验,背后是无数台服务器在负载均衡 、互相备份 、限流熔断...
他们比你还累。