二层看 MAC、三层看 IP,四到七层看的是内容:端口号、URL、Cookie。 看得越深,能力越强,代价是性能与加密的墙。
1.9.5 的路由器「只管把包送到」,这一层开始管「送给谁、怎么分」。 这一节讲的是负载均衡器的原理,1.9.7 讲网关。
一、看内容意味着什么
每往上一层,能看的东西就多一层:
| 层 | 看什么 | 能做的决定 |
|---|---|---|
| 二层 | MAC | 往哪个口转发 |
| 三层 | IP | 往哪个网段转发 |
| 四层 | 端口号 | 是 HTTP 还是数据库 |
| 七层 | URL、Cookie、头 | 按用户、按路径分发 |
第三行是「四层」的分水岭:只看端口号就能分出「这是网页、这是数据库、这是邮件」,
这已经能支撑大部分分流需求了。
而且四层不需要解析应用协议,性能高得多。
第四行才是「七层」的价值:
按 URL 把 /api 转给一组服务器、/static 转给另一组;按 Cookie 做灰度发布。
这些都必须看到应用层内容才能做到。
代价是必须完整解析,而且加密之后看不到了。
二、三种分发算法
同样三台服务器,三种算法给出三种分布:
| 算法 | 依据 | 分布 |
|---|---|---|
| 轮询 | 按顺序挨个发 | S1:4/S2:4/S3:4 |
| 最少连接 | 看谁当前连接少 | S1:6/S2:4/S3:2 |
| 源地址哈希 | 同一用户固定一台 | S1:1/S2:5/S3:6 |
三种数字都不一样,正好说明各管一件事: 轮询保证「次数均匀」,哈希保证「同一用户总是同一台」。 后者对「有会话状态」的应用是必须的——否则第一次登录在 S1、第二次请求到 S2,会话就丢了。 第二行那个「最少连接」看起来最聪明,但它有代价: 必须实时统计连接数,这本身有开销;而且短连接场景下统计是滞后的—— 刚分给 S1 的请求还没建立连接,统计里看不出来。 所以真实负载均衡器通常是「加权轮询 + 健康检查」的组合。 第三行那个哈希有个明显的坑:服务器数量一变,几乎所有用户的落点都会变, 缓存全失效、会话全丢。 这就是「一致性哈希」被发明出来的原因——它把「增删一台机器」的影响限制在一小部分 key 上。
三、看得越深,代价越大
三层代价,一层比一层硬:
| 代价 | 表现 | 缓解 |
|---|---|---|
| 性能 | 要解析、要缓冲 | 硬件卸载 |
| 加密 | TLS 之后看不到内容 | 在网关处解密 |
| 状态 | 要记住会话 | 会话同步/粘性 |
第二行是今天最现实的一道墙:七层设备要看 URL,就必须先解密。 所以它变成了「TLS 的终点」——客户端到它这一段是加密的,它到后端那一段可能又是加密的。 这也意味着「七层设备能看到你所有的明文」,所以它同时是一个巨大的信任点与风险点。 第三行那个「状态」是可用性的隐患:多台七层设备之间要同步会话,否则用户换一台就掉登录。 同步本身又是一条要保证的链路——这就是「设备越聪明,架构越复杂」的典型例子。 所以选型的原则很简单:能用四层解决就别上七层。 按 IP 或端口能分清的,就不必去解析 HTTP。
四、动手 Demo:四到七层交换机,其实是「按内容分发」
模拟三种分发算法,看十个请求怎么落到三台服务器上。 三台的容量故意设成 6/4/2,才能看出「最少连接」的差别。
import zlib
caps = {'S1': 6, 'S2': 4, 'S3': 2}
reqs = ['u%d' % i for i in range(12)]
rr = {k: 0 for k in caps}
for i, _ in enumerate(reqs):
rr[list(caps)[i % 3]] += 1
lc = {k: 0 for k in caps}
for _ in reqs:
k = min(lc, key=lambda x: (lc[x] / caps[x], lc[x]))
lc[k] += 1
hh = {k: 0 for k in caps}
names = list(caps)
for r in reqs:
hh[names[zlib.crc32(r.encode()) % 3]] += 1
for tag, d in (('轮询', rr), ('最少连接', lc), ('源地址哈希', hh)):
print('%-10s %s' % (tag, '/'.join('%s:%d' % (k, d[k]) for k in caps)))
实跑输出:
--------------------------------------------------------------------
四到七层交换机,其实是「按内容分发」
--------------------------------------------------------------------
**二层看 MAC、三层看 IP,四到七层看的是「内容」**——**端口号、URL、Cookie,甚至 HTTP 头。**
下面模拟三种常见的分发算法,看流量分布:
算法 依据 分布
-------------------------------------------------------------
轮询 **按顺序挨个发** S1:4/S2:4/S3:4
最少连接 **看谁当前连接少** S1:6/S2:4/S3:2
源地址哈希 **同一用户固定落到一台** S1:1/S2:5/S3:6
**三行的数字都不一样,正好说明三种算法各管一件事**:
**轮询保证「次数均匀」,哈希保证「同一用户总是同一台」。**
**后者对「有会话状态」的应用是必须的**:**否则第一次登录在 S1、第二次请求到 S2,会话就丢了。**
**「最少连接」看起来最聪明,但它需要实时统计连接数**,
**这本身就有开销,而且短连接场景下统计滞后。**
**所以真实负载均衡器通常是「加权轮询 + 健康检查」的组合。**
轮询是 4/4/4(12 个请求平均分),最少连接是 6/4/2(按 6:4:2 的容量比例分),
哈希是 1/5/6(按 crc32 取模,分布不均)。
哈希那一行最能说明问题:它保证「同一个 key 总落同一台」,但不保证「负载均匀」。
十二个 key 分到三台,本来就是随机的,偏成这样很正常。
所以哈希适合「要粘性」的场景,不适合「要均衡」的场景。
最少连接那一行能分出 6/4/2,是因为把容量写进了比较式:
lc[x] / caps[x] 表示「当前负载占容量的比例」,取最小的那台。
如果三台容量相同,它就退化成「平均分」。
30 秒实验
- 把
caps三台都改成 4,再跑一遍——看最少连接是不是变成 4/4/4。 - 把
reqs从 12 个改成 100 个,看哈希那一行会不会更均匀。 - 想一下:如果服务器从 3 台变成 4 台,哈希那一行会有多少 key 换台?
常见误区
- 以为四层与七层只是「快慢」的差别。 能力不同:四层只能看端口,七层能看 URL 与 Cookie。
- 以为负载均衡就是「平均分」。 哈希与最少连接都不平均, 因为它们的目的一开始就不是「平均」。
- 以为七层设备总是更好。 它要解密、要解析、要保持状态,能用四层解决就别上七层。
自测三问(点开对答案)
1. 四层与七层设备分别看什么? —— **四层看端口号,七层看 URL、Cookie、HTTP 头等内容。**看得越深能力越强, 代价是性能与加密的墙。 2. 为什么「源地址哈希」不适合要求负载均衡的场景? —— **它保证「同一 key 落同一台」,但不保证均匀。**而且服务器数量一变,几乎所有 key 都会换台。 3. 七层设备最大的现实障碍是什么? —— **TLS 加密之后看不到内容。** 所以它必须成为解密终点,这既是性能开销, 也是巨大的信任与风险点。下一节讲最后一个设备:网关——它不只是转发,它要翻译。
- ☐ (本节完,下一节 1.9.7 网关)