负载均衡 LB
第 5 章讲到这里,四位主角已经出场:交换机、路由器、网关与光猫、防火墙,它们分别解决了"同一条街怎么送""跨城怎么送""出门那道门""谁能进门"。但还有一个问题没回答:当一个网站的访问量大到一台服务器根本接不住的时候,怎么办?答案是:多买几台服务器,然后在它们前面站一个"分单员",把涌进来的请求分给不同的服务器——谁也别闲着、谁也别累趴。这个分单员,就是本节的主角:负载均衡(Load Balancer,简称 LB)。这一节用"奶茶店门口的分单员 / 银行的叫号机",把四层与七层、五种分发算法、健康检查、会话保持全部讲透,也为整个第 5 章收官。
想象一家周末爆满的奶茶店。如果店里只有一位收银员,队伍会从柜台一直排到门外,店员手忙脚乱,顾客骂骂咧咧——这就是"一台服务器扛不住"的样子。
聪明的店会把柜台开成三个窗口。门口站着一个分单员:每个客人进门,他扫一眼三个窗口的排队情况,喊一声"3 号窗口";哪个窗口空了,就把下一位客人派过去;1 号窗口挂出"设备检修"的牌子,他就绕开它;有位熟客每次都想去 2 号窗口,因为那儿的店员记得他的口味,分单员也记住了,总把他往 2 号窗口引。
这一节要讲的负载均衡,就是这个分单员的职业化版本。它干的事一句话:把请求(客人)分给不同的后端服务器(窗口),让整个系统既不让任何一台累趴,也不让任何一台闲着。
先把这一节的术语翻译成人话
和前面几节一样,这一节也是术语密集。好消息是:负载均衡的每个概念,几乎都能在"奶茶店分单员 / 银行叫号机"这套场景里找到一一对应的东西。先过一遍这张表,读完全文再回来重读一次,效果最好。
| 术语 | 换成大白话 | 生活里对应的东西 |
|---|---|---|
| 负载均衡(Load Balancer,缩写 LB) | 说白了就是"站在服务器门口的排单员" | 奶茶店门口喊"3 号窗口"的分单员 |
| 后端服务器(Backend Server) | 说白了就是"真正干活的那几台机器" | 窗口后面做奶茶的店员 |
| 虚拟 IP(Virtual IP,VIP) | 说白了就是"对外只亮一个公共门牌" | 店门口挂的"本店"招牌,不问后面有几个窗口 |
| 四层负载均衡(L4) | 说白了就是"只看你取的是几号窗口" | 叫号机只看号,不看你要办什么业务 |
| 七层负载均衡(L7) | 说白了就是"听你要办什么事再分" | 医院分诊台:问清哪不舒服,再分到内科还是外科 |
| 轮询(Round Robin) | 说白了就是"挨个叫号,轮流来" | 窗口 1、2、3、1、2、3 轮流接客 |
| 加权轮询(Weighted Round Robin) | 说白了就是"老手多接、新手少接" | 老师傅窗口接 3 个,实习生窗口接 1 个 |
| 最少连接(Least Connections) | 说白了就是"谁空闲派给谁" | 理发店:哪把椅子空着,新客人就去哪 |
| IP 哈希(IP Hash) | 说白了就是"同一个 IP 永远分到同一台" | 熟客总去 2 号窗口,因为那儿的店员记得他 |
| 一致性哈希(Consistent Hashing) | 说白了就是"加窗口、减窗口,影响最小" | 公交环线上新加一站,只有离它最近的那段乘客受影响 |
| 健康检查(Health Check) | 说白了就是"定期确认这窗口还开着吗" | 分单员隔几分钟喊一嗓子"都还开着吗" |
| 心跳(Heartbeat) | 说白了就是"窗口自己主动举手报到" | 店员每做几杯就举一下手"我还在" |
| 会话保持(Sticky Session) | 说白了就是"同一个客人的一串事,始终在同一窗口办" | 熟客的订单永远记在 2 号窗口 |
| 故障转移(Failover) | 说白了就是"窗口塌了,客人转给别的窗口" | 1 号窗口停电,客人全部改去 2、3 号 |
这一节可以用一句话串起来:负载均衡是"奶茶店门口的分单员",它对外只亮一个门面(虚拟 IP),对内按一套规则把请求分给一群后端服务器(分发算法),并且时不时确认每台后端还活着(健康检查)。其余全是细节。
一台服务器为什么扛不住:先把问题看明白
要理解负载均衡,得先理解它的反面:一台服务器为什么扛不住?回指 § 4.5,一个请求从发出去到拿到响应,成本由两部分组成:一个网络来回的时间(由距离决定)加上服务器处理的时间(由服务器性能和业务复杂度决定)。问题在于:网络来回的时间是"一个请求一份",而服务器处理的时间是"大家抢着用"的。
打个比方,食堂打饭:一个窗口一分钟能做 10 份饭。中午全校 3000 人同时下课涌过来,一个窗口一分钟只能接 10 份——后面的同学只能干等,队伍越来越长。换成大白话:请求来得太快,服务器处理不过来,后面的人就得排队,这就是排队时延(§ 1.6 讲过时延的组成)。
具体数字换成人话:一台普通服务器每秒能处理的请求数因配置而异,量级大概在几百到几千(经验量级,具体因机器而异)。而一个热门网站的高峰请求量可以到每秒几万甚至几十万——相当于一个打饭窗口要面对整座城市的人。别说一秒,一眨眼都接不住。一台服务器扛不住,通常有这三个症状,一个比一个严重:
- 变慢请求排着队等处理,每个请求的响应时间直线上升。好比食堂窗口排队的人越来越多,每个人打饭要等的时间越来越长。
- 超时等得太久,用户那边先放弃了(回指 § 4.4 的 TLS 握手、§ 3.2 的 TCP 重传,都有超时机制)。相当于排队排了 40 分钟,你饿得受不了,走了。
- 直接拒绝服务器连队都不让排了,直接返回一个错误状态码(回指 § 4.5:5xx 表示服务器这边出了事,比如 503 Service Unavailable)。相当于窗口直接挂出"今日售罄"。
那换个思路:买一台更贵的服务器行不行?这叫垂直扩展(Scale Up)——把小面包车换成重卡。有用,但有天花板:单台机器的 CPU、内存、带宽都有物理上限,而且越往上越贵。换成大白话:一辆卡车再大也有载重上限,而且重型卡车贵得离谱。
于是有了另一条路:水平扩展(Scale Out)——买 100 辆小面包车并行送货。对应到网站,就是多买几台服务器,让它们一起干活。可是新问题来了:客人怎么知道该找哪辆车?总不能要求用户自己记住"我这次去 3 号服务器"。总得有个东西站在前面,替客人做选择。这个东西,就是负载均衡器。
【单机 vs 集群 + 负载均衡:一张图看懂】
单机时代: 集群时代:
用户 ──→ 一台服务器 用户 ──→ 负载均衡器(一个门面)
├─→ 服务器 A
├─→ 服务器 B
└─→ 服务器 C
一台挂了 → 全站没了 一台挂了 → 其他两台继续接客
流量一涨 → 排队到崩溃 流量一涨 → 加两台机器就扛住
注意集群那一侧的关键词:"一个门面"。用户面对的永远只有一个入口,至于入口后面站着几台服务器、今天是不是又加了两台,用户完全无感。这个"门面"就是负载均衡器存在的意义——它把"一群服务器"包装成"一台服务器"给你用。
用下面的时延账本自己算一笔:假设请求越来越多,把排队的时间加进去,看看总时延是怎么涨上去的——你会直观看到"一台服务器面对一大群人"时,时间是怎么被队伍吃掉的。
分单员到底在干什么:负载均衡的三件事
负载均衡(Load Balancer)这个词听着玄,其实就是"站在服务器门外的分单员"。它的完整职责可以拆成三件事,记住这三件事,你就抓住了它的全部工作:
- 对外只亮一个门面用户只需要知道一个地址(域名或 IP)。这个对外统一的入口有个专业名字叫虚拟 IP(Virtual IP,简称 VIP)——说白了,就是店门口那块"本店"招牌:你只管进店,不用管后面有几个窗口。
- 接住每个请求,按规则挑一台后端每个请求到了,负载均衡器按一套规则(分发算法,下一节就讲)挑一台后端服务器送过去。它自己不动手处理业务,只负责"派活"。
- 定期检查后端还活着没有如果一台后端已经宕机,还继续往它那儿派请求,用户就卡死了。所以负载均衡器要定期确认每台后端的状态——这就是健康检查,后面有专节。
这里最关键的一点是:对用户来说,一切透明。用户只跟"一个门面"打交道,门面背后是 10 台还是 100 台服务器,他完全没有感觉。就好比你去银行办业务:取号、排队、被叫号,你根本不会去数大厅后面到底有几个柜员在忙。
顺便澄清一个常见误会:负载均衡器自己很轻。它不处理业务逻辑、不查数据库、不做奶茶,它的全部价值在于"让后面那排服务器忙得均匀"。换成大白话:分单员自己不动手做奶茶,他只是保证三个窗口都不闲着、都不累趴。这也意味着,加一台负载均衡器不等于加了算力——它只是让已有的算力被用得更均匀(这一点在"常见误区"那节还会回来说)。
还有一点值得注意:负载均衡器挑出后端之后,就把请求原样转过去——这个"选下一棒"的动作,和 § 5.2 路由器查路由表选下一跳的思路一模一样。都是"不掌握全程,只决定下一步交给谁":路由器把包交给下一跳就不管了,负载均衡器把请求交给某台后端也就不管了(除非它同时是七层反向代理)。不妨这样想:路由器是"跨城接力"的分单员,负载均衡器是"同一栋楼里"的分单员。
如果你只记住这一节的一个类比,请记住这个。
一家奶茶店开出了三个制作窗口(1 号、2 号、3 号),每个窗口后面各有一位店员在调奶茶。门口站着分单员。
① 他只看排队情况,不关心客人点了什么。客人进门,他只需要判断"哪个窗口最空、该去几号"——至于这杯是珍珠奶茶还是柠檬茶,那是店员的事。四层负载均衡就是这样:只看"这个请求要发往哪个 IP 和端口"(送到哪个窗口),不拆开看内容。
② 他只负责分配,自己不动手做奶茶。分单员不碰原料、不做饮品,他的全部价值在于让三个窗口忙得均匀。负载均衡器也一样:它不处理业务逻辑,只负责把请求"调度"到合适的服务器——真正的体力活在后面那排服务器身上。
③ 哪个窗口挂了,他必须知道。如果 1 号窗口的机器坏了还继续往里派客人,客人会堵死在窗口前。所以分单员要么每隔一会儿喊一声"都还开着吗",要么让每个窗口主动举一下手——这就是健康检查。
把这三个特征记住,这一节就学完一半了:负载均衡器 = 一个门面(VIP)+ 一套分派规则(分发算法)+ 一套健康检查。
四层 vs 七层:只看窗口号 vs 听你要办什么
现在到了这一节最重要的一张牌:四层(L4)与七层(L7)负载均衡。这两个数字不是随便起的,它们来自前面讲过的分层模型:四层对应 OSI 七层模型(§ 2.1)里的第 4 层"传输层"(在 TCP/IP 四层模型(§ 2.2)里叫传输层),七层对应第 7 层"应用层"(在四层模型里叫应用层)。数字的含义是"负载均衡器在协议栈的哪一层做决定"。
四层负载均衡(L4):它只看到 IP 地址和端口号(比如 203.0.113.88:443),然后把这个连接原样转给某一台后端。它不拆开包裹看里面的内容。说白了,它就像只认号的叫号机:客人来了,看一眼号单上是"A 类还是 B 类",就告诉你去 1-3 号窗口还是 4-6 号窗口——至于你要办存款还是贷款,它不管,那是窗口里柜员的事。
七层负载均衡(L7):它能拆开 HTTP 报文(回指 § 4.5:请求行、请求头、请求体),看到 URL 路径、Host 域名、Cookie、甚至请求体里的内容。于是它能做更精细的决策——比如"图片请求去图片服务器组,下单请求去订单服务器组"。说白了,它像医院的分诊台:护士先问你哪儿不舒服,再决定把你分到内科、外科还是眼科。一个只看"送到哪个窗口",一个听"你要办什么事"——这就是 L4 和 L7 最本质的区别。
代价与收益也很直白:L4 不拆包,所以快、省资源、能扛海量连接(每秒百万级连接是常见量级,具体因设备而异);L7 要拆包解析,所以慢一点、吃 CPU,但换来"按内容分流"的灵活。真实世界里两者经常串起来用:入口先放一台 L4 快速分流,把请求按端口分到不同的服务器组;到了服务器组里面,再用 L7 按 URL 做细粒度分流。这就像机场:先按航班号把你分到 A 区还是 B 区(粗分),登机口再按座位号细查(细分)——分层接力,和 § 2.1/§ 2.2 讲的分层思想一脉相承。
| 四层 L4 | 七层 L7 | |
|---|---|---|
| 在哪一层做决定 | 传输层(§ 2.2 四层模型的第 3 层) | 应用层(§ 2.2 四层模型的第 4 层) |
| 看什么信息 | IP 地址 + 端口号 | HTTP 内容:URL、Host、Cookie、请求体 |
| 会不会拆包 | 不拆,原样转发 | 要拆开报文看内容 |
| 速度与开销 | 快、省资源 | 慢一点、吃 CPU |
| 能做什么精细决策 | 按端口分流(如 443 去 HTTPS 组) | 按路径/域名/Cookie 分流(如 /img 去图片组) |
| 生活类比 | 叫号机只看号单类型 | 分诊台护士听你描述病情 |
一个技术标注:这里说的 HTTP 规范编号是 RFC 9110(回指 § 4.5:请求报文、头部字段、状态码都定义在这一系列文档里)。而"L4 负载均衡""L7 负载均衡"这两个词本身没有自己的 RFC——它们不是独立的协议,而是"在协议栈第几层做转发"的两种工作方式,身份来自 § 2.1/§ 2.2 的分层模型本身。
还有一个实战中绕不开的细节:七层负载均衡器常常顺便当一把"反向代理"(Reverse Proxy)。这个词在 § 4.5 的术语表里出现过——反向代理说白了就是"你以为在跟店家说话,其实先经过了一个前台":用户对着负载均衡器说话,负载均衡器再替他去跟后端说话,把后端服务器的真实身份完全藏起来。既然七层 LB 本来就要拆开 HTTP 看内容,它往往就把 HTTPS 的加解密也顺手做了——这招叫 TLS 终结(也叫 SSL offload,回指 § 4.4 的 TLS 握手):用户和 LB 之间走加密,LB 和后端之间可以是明文,后端每台机器都省掉了做加解密的开销。好处是省算力,代价是"加密只到 LB 为止"——如果 LB 和后端之间的链路不安全,这段内容就相当于裸奔。所以正经部署里,LB 到后端之间通常走加密或至少走内网,这个细节在安全要求高的系统里很重要。
分发算法之一:轮询与加权轮询
负载均衡器接到一个请求,到底分给谁?靠的是分发算法(也叫调度算法)。这一节开始逐个拆,每个都配一个生活类比。
最朴素的算法是轮询(Round Robin):挨个叫号,1、2、3、1、2、3,轮流来。三个窗口,第一个客人去 1 号,第二个去 2 号,第三个去 3 号,第四个又回 1 号……大家机会均等,谁也别想抢戏。打个比方,食堂开饭,几个窗口轮流叫号;或者洗车店三个工位轮着接车,一辆一辆排过去。
轮询的好处是简单、公平、不用动脑子;但它有个前提假设:所有后端都一样——配置一样、能力一样、每个请求的重量也一样。现实里这个假设经常不成立。想象 1 号窗口是干了十年的老师傅,一杯奶茶 30 秒搞定;2 号窗口是第一天上班的实习生,一杯要 2 分钟。轮询还是"一人一杯",结果老师傅窗口闲得擦台子,实习生窗口排成长队。
于是有了加权轮询(Weighted Round Robin):给每个后端一个权重,权重大的多分活。换成大白话:老师傅窗口权重 3,实习生窗口权重 1,那么每 4 个请求里,3 个给老师傅、1 个给实习生。这样快的窗口多干活,慢的窗口少干活,整体吞吐反而最大。就像流水线(车间)上,快手多分几件活、慢手少分几件,整条线才跑得最快;也像外卖平台给熟手骑手多派几单。
什么时候用轮询系?当你的后端机器配置差不多、而且每个请求消耗的资源也差不多的时候。典型的例子:一堆相同配置的服务器,接口都是"读一个缓存再返回",每个请求耗时都差不多——轮询就是最合适的选择,简单又够用。
分发算法之二:最少连接
轮询有个看不见的坑:它假设"每个请求的重量都一样"。但真实网站的请求,轻重悬殊得很:有的请求看一眼就完(比如一张静态图片),有的请求要查半天(比如一次复杂的搜索、一个大数据量的报表)。如果轮询平均派发,很可能把好几个"重请求"都派到同一台机器上——这台机器累死,别的机器闲死。
于是有了最少连接(Least Connections):新来的请求,给"当前正在处理的连接数最少"的那台后端。说白了就是"谁空闲,就派给谁"。哪个窗口手上的活儿最少,下一位客人就去哪个窗口。
打个比方,理发店不太可能用"挨个叫号"——你更愿意去那个空着的椅子,或者手头客人最少的师傅那儿。餐厅里,领班会把手上的活儿分给"手上桌数最少"的服务员,而不是机械地轮一圈。这些都是"最少连接"的直觉:看谁闲,给谁派活。
为什么它比轮询更能反映真实负载?因为"正在处理的连接数"是动态的:一台机器刚处理完一个重请求,它的连接数就降下来了,下个请求就会被派给它;一台机器正被一个大请求卡住,它的连接数一直没降,就不会再被派新活。换成大白话:轮询是按人头轮流,最少连接是按忙闲分配——后者的信息量更大。
不过它也有局限:连接数并不完全等于"忙不忙"——一个连接可能极轻,也可能极重。所以工程上还演化出更精细的变体:加权最少连接(再叠加机器的能力差异)、按响应时间、按 CPU 使用率、按内存使用率……这些是各家实现不同的工程实践细节,这里不展开编造数值。你只需要记住:从"轮询"到"最少连接",本质上是"从只看人头,到看忙闲"的进步。
分发算法之三:IP 哈希——同一客人总去同一窗口
前面两种算法(轮询、最少连接)都不关心"请求是谁发来的"。但有些场景偏偏需要"同一伙人总去同一台服务器"。这就轮到IP 哈希(IP Hash)登场了。
它的做法很简单:把请求的来源 IP 地址,通过一个哈希函数算出一个数字,再用这个数字决定分给哪一台后端。同一个 IP 永远算出同一个数字,所以同一个用户(至少是同一个 IP 的用户)永远被分到同一台服务器。
哈希(Hash)这个词听着玄,其实就是"把任意输入,变成一个小范围内的数字"。打个比方,图书馆把每本书的书名翻译成一个架号(分类号)——同一本书永远落在同一排书架上,但随便换一本书,架号就完全不同。哈希函数干的就是这件事:输入"你家 IP",输出"3 号服务器";输入"我家 IP",输出"7 号服务器"。
IP 哈希的价值在于:它是"天然的会话保持"。同一个用户连续发来的请求,都会被分到同一台服务器,不需要 Cookie、不需要额外记忆,全靠算。换成大白话:分单员记住"这位熟客总去 2 号窗口"——只不过他记的不是人,而是这个人的 IP。
但它的缺点也很明显:① 某台服务器挂了,所有被哈希到它的用户集体"改道"或遭殃;② IP 本身不稳定——家里上网的 IP 是运营商动态分配的,换个 WiFi、手机从 WiFi 切到 4G/5G,IP 就变了(回指 § 3.5:NAT 还会让一屋子设备共用同一个公网 IP)。相当于熟客换了件衣服,分单员就认不出来了,又把他当成新客随便派。
所以 IP 哈希通常用在"不需要精确会话、只想让同一用户尽量稳定"的场景,或者在更复杂的哈希变体里当零件。它本身不算高级,但它是理解下一个算法(一致性哈希)的垫脚石。
分发算法之四:一致性哈希——加减机器影响最小
哈希派发有个隐藏的大问题,普通哈希完全没解决。先看普通哈希怎么算:假设有 5 台服务器,编号 0 到 4,把 IP 哈希出来的数字除以 5 取余数,余数就是服务器编号。一切正常,直到——你加了第 6 台服务器。
一改成"除以 6 取余数",几乎所有人的余数都变了:本来该去 3 号的人,可能改去 0 号;本来该去 4 号的人,可能改去 2 号。换成大白话:学校按"学号除以班级数取余数"分班,班级数从 5 个变成 6 个,几乎全班同学的班级都要重分一遍。对缓存类系统这是灾难:所有缓存全部"够不着"了(缓存分布在各台机器上,用户改去了别的机器,就命不中缓存),数据库瞬间被查爆——这就是传说中的"缓存雪崩"。
一致性哈希(Consistent Hashing)就是来解决这个问题的。它的思路是:把所有服务器和所有请求,都放到一个巨大的"数字圆环"上(0 到 2 的 32 次方减 1,可以理解成一圈刻度)。每台服务器在环上占一个点;每个请求也算出一个点,然后"顺时针"找第一个遇到的服务器——它就是接收者。
【一致性哈希:一个圆环上的故事】
把 0 ~ 2^32-1 想象成一圈刻度,首尾相连:
0 ──────────────── 服务器A(100)
│ │
服务器B(50) 服务器C(150)
│ │
└──── 请求1(80) ──────┘
请求 1 落在刻度 80,顺时针遇到的第一个服务器是 C(150)
→ 请求 1 由服务器 C 处理
现在新增一台服务器 D(90),正好落在 80 和 100 之间:
原本去 A(100) 的、落在 (80, 90] 这一小段的请求,改去 D;
其余所有请求完全不动。
现在看加机器的影响:新服务器只"接住"它和它逆时针方向前一台之间的那一段请求,其他请求一个都不动。打个比方,环形公交线路上有 A、B、C、D 四个站,你住在 C、D 之间,每天坐环线在下一站换乘。现在线路上新加了一个站 X(恰好加在 C 和 D 之间):受影响的只有"本来要在 C 站换乘、现在能在 X 站换乘"的那一小段乘客——离新站最近的那部分人。其他人该怎么坐还怎么坐。这就是"加机器只影响一小部分请求"的全部秘密:影响范围 = 新机器到它前面那台机器之间的那一小段圆弧。
减机器同理:一台机器下线,只有"它顺时针方向的下一个区间"要接住它的活,别的不受影响。普通哈希加一台机器,几乎所有人重新洗牌;一致性哈希加一台机器,只有一小段圆弧重排——这就是两者的本质区别。
| 普通哈希 | 一致性哈希 | |
|---|---|---|
| 怎么定映射 | 取余数:hash % 服务器数量 | 环上落点 + 顺时针找最近服务器 |
| 加一台机器 | 几乎所有人的映射都变 | 只有一小段圆弧的请求变 |
| 减一台机器 | 几乎所有人的映射都变 | 只有被减机器顺时针的下一段受影响 |
| 缓存/连接受影响面 | 几乎全部失效 | 只有一小部分需要重排 |
| 生活类比 | 按学号除以班级数分班,改班级数全班重分 | 公交环线加一站,只有邻近那段的乘客换乘变 |
技术标注,这句要划重点:一致性哈希是工程实践概念,没有对应的 RFC——它最早出自 1997 年 Karger 等人关于分布式缓存的一篇论文,后来被无数系统(分布式缓存、负载均衡、消息队列)广泛采用,但它从来不是 IETF 标准化的协议,也没有一个叫"一致性哈希"的 RFC 编号。你在文档里看到 "consistent hashing",知道它是一种算法思路就够了。
工程上还有一个常见的补丁叫虚拟节点:让每台真实服务器在环上不占一个点,而是占好几个点(分散着放)。说白了,就是让每台机器的"势力范围"在环上铺开,这样即使几台机器恰好挤在一起,也不会出现"环上一大半归一台管、别的闲死"的极端情况。这也是工程实践的细节,各家实现不尽相同——你只需要记住:环上的点是可以人为撒的,撒得均匀,分配就均匀。
回到负载均衡:用一致性哈希做分发的好处,正是"扩容缩容时,现有连接和缓存尽量不受影响"——这也是为什么缓存集群几乎必用一致性哈希。一句话记法:普通哈希怕"变数",一致性哈希为"变数"而生。
健康检查:分单员怎么知道哪个窗口还开着
无论用哪种算法,都有一个前提:派活的那台后端必须是活的。如果分单员把客人派给一个已经塌了的窗口,客人会卡死在那里。所以负载均衡器必须定期确认每台后端的状态——这就是健康检查(Health Check)。
健康检查有两种主流做法:
- 主动探测(Active Check)负载均衡器每隔几秒(具体间隔因配置而异)主动向后端发一个探测。四层负载均衡就试"TCP 端口连不连得上"(回指 § 3.2 的三次握手:连得通,说明端口活着);七层负载均衡就发一个 HTTP 请求(比如
GET /healthz),期待返回 200(回指 § 4.5 状态码)。好比分单员隔几分钟朝各窗口喊一嗓子"都还开着吗?" - 被动检测(Passive Check)后端自己主动报告"我活着"——这叫心跳(Heartbeat);或者负载均衡器发现"连续 N 次把请求发给它都失败了",就自动把它摘掉。前者好比每个窗口每做几杯就举一下手"我还在";后者好比"叫了三次没应,就当你挂了"。
发现不健康之后呢?摘除(drain / remove):负载均衡器不再往这台后端派新请求。好比窗口挂出"暂停服务"的牌子,分单员绕开它。等它恢复健康(连续几次探测成功),再翻回"营业中"的牌子,重新接客。这个"摘除—恢复"的循环,是负载均衡器区别于"一个只会转发的交换机"的核心——没有健康检查,再好的算法也会把请求发给死掉的服务器。
顺带认识一个状态码:当所有后端都挂了或都在维护时,负载均衡器自己会向用户返回 503 Service Unavailable(服务不可用,回指 § 4.5:5xx 是服务器这边出了问题)。用下面的组件,点开这个状态码以及几个相关状态码,看看它们的官方含义:
一个容易忽略的细节:健康检查是有延迟的。探测是"每隔几秒"一次,不是每毫秒一次。所以一台后端刚挂掉的那几秒里,可能还有几个请求被派给它——这几个请求就失败了。这解释了为什么生产环境总说"负载均衡不能保证零故障,只能保证故障影响面小"。
会话保持:登录状态怎么办
现在把前面所有知识拼起来,看一个真实场景:你在购物网站登录了,然后点了下一页,结果被要求重新登录。怎么回事?
回指 § 4.5:HTTP 是无状态的,登录状态靠 Cookie 维持。服务器在响应里塞一个 Set-Cookie,浏览器记住这个"寄存牌",下次请求带上它。问题在于:寄存牌(Cookie)在你手里,但寄存柜(服务器上的会话数据)可能只有某一台服务器有。如果第一次请求去了服务器 A,A 把"你已登录"记在自己那儿;第二个请求被负载均衡分到了服务器 B——B 翻了翻自己的抽屉,没你的档案,只好让你重新登录。用户会崩溃的。
解决这个问题的办法有三条路,正好对应三种不同的哲学:
- 路一:sticky session(粘性会话 / 会话保持)负载均衡器记住"这个用户永远去 A"。好比熟客固定去 2 号窗口,因为那儿的店员记得他的口味。缺点:① 用户扎堆——某个热门用户可能一个人就把 A 撑爆,别的机器闲死,违背了负载均衡"均摊"的初衷;② A 挂了,这位用户的会话跟着没了——寄存柜烧了,牌也没用。
- 路二:共享会话存储把登录状态放到所有服务器都能访问的地方(专门的会话服务器、数据库、或内存数据库)。好比所有窗口共用一个"顾客档案柜",谁接待都能查档。这是现代网站的主流做法——服务器随便加、随便减,会话数据都在公共档案柜里。
- 路三:无状态 + 客户端存储服务器干脆不存会话,把状态打包进 Cookie 本身(现在的令牌 Token 方案大体是这个思路),任何一台服务器拿到都能验。好比把档案做成一张卡片,让顾客自己随身带着,到哪个窗口都能验。回指 § 4.5 那句结论——"Cookie 的意义是把记忆负担从服务器转移给客户端"——现在你能完整接上这句话了。
| sticky session | 共享会话存储 | 无状态 + 客户端存储 | |
|---|---|---|---|
| 会话数据存在哪 | 某一台后端服务器 | 所有后端都能访问的公共存储 | 客户端自己的 Cookie / 令牌里 |
| 任何一台后端都能接客吗 | 不能,只能去被粘住的那台 | 能,谁都能查档 | 能,谁都能验卡 |
| 那台后端挂了会怎样 | 这位用户的会话一起没了 | 换一台照样服务 | 换一台照样服务 |
| 负载均衡分配自由度高吗 | 低,被"粘住"束缚 | 高,随便分 | 高,随便分 |
| 生活类比 | 熟客固定窗口,师傅记得口味 | 所有窗口共用一个档案柜 | 顾客随身带卡片,到哪都能验 |
注意一个常见混淆:"会话保持(sticky session)"只是三种方案里的第一种,很多人却把它当成唯一的办法。实际上现代大型网站几乎不用纯 sticky,因为"把用户粘死在一台机器上"恰恰破坏了水平扩展的意义(机器一挂,用户会话就没了;机器一多,分配就不均匀)。主流做法是共享会话存储或无状态方案——让"任何一台服务器都能服务任何用户"。
负载均衡器长什么样:硬件、软件、云
负载均衡是一个"角色",不是某一种特定的盒子。同一个角色,有三种常见的"扮相":
| 硬件负载均衡 | 软件负载均衡 | 云负载均衡 | |
|---|---|---|---|
| 典型代表 | F5 BIG-IP、Citrix Netscaler 等专用设备 | Nginx、HAProxy、LVS 等程序 | AWS ELB、阿里云 SLB、腾讯云 CLB 等 |
| 形态 | 一台专用的硬件盒子 | 装在普通服务器上的软件 | 云平台提供的托管服务 |
| 优点 | 吞吐大、稳定、有专用芯片加速 | 便宜、灵活、可深度定制 | 按量付费、自动扩缩、免运维 |
| 缺点 | 贵、升级要换硬件 | 要自己装、自己调、自己养 | 绑定云厂商、配置自由度有限 |
| 生活类比 | 机场专用安检设备:贵但快 | 手机装个叫号 App:便宜灵活 | 活动请的兼职分单员:按小时算钱 |
还有一个最古老也最土的办法:DNS 轮询。回指 § 4.2 的 DNS 解析:一个域名本来对应一个 IP,DNS 轮询让一个域名对应多个 IP,DNS 服务器每次解析时轮流返回其中一个。好比校门口问路,问十次保安,他轮流给你报十个不同宿舍楼。缺点很致命:DNS 有缓存(§ 4.2 讲过 TTL),服务器挂了不能立刻让全世界知道——所以它只能算"入门级负载均衡",正经网站还是会把 LB 放在 DNS 后面。
记住:硬件、软件、云,只是同一个"分单员"的三种扮相——背后的原理(门面、算法、健康检查)完全一样。你学会的是原理,具体选哪款,看预算、看规模、看运维能力。
现实场景:网站集群、CDN,还有你家
现在把视野拉回真实世界,看负载均衡都出现在哪里。
第一站:网站集群。大型网站从来不是"一台服务器",而是一大群。回指 § 4.5 那张请求链路图:"一个请求要过 CDN → 负载均衡 → 网关 → 应用 → 数据库好几道手"——现在你认识其中每一个角色了。负载均衡就站在"网关"前面,把海量请求分给后面一队应用服务器。大促之前,运维把几十台新服务器接进集群,负载均衡的健康检查自动发现"新窗口开业",开始往它们那儿派活——整个过程用户毫无感觉。这就是水平扩展最迷人的地方:加机器 = 加窗口,分单员自动认识它们。
第二站:CDN 边缘节点。回指 § 1.6 讲过的时延与带宽:内容离用户越远,时延越大。CDN 的干法是把内容缓存到全国各地的边缘节点上,让用户就近取货。但每个边缘节点本身也是一群缓存服务器——用户被调度到哪个节点、节点内的哪台服务器,背后又是一层负载均衡,只不过衡量的维度多了一个"距离"。CDN 解决"内容离你远不远",负载均衡解决"服务器忙不忙",两者配合:命中缓存的请求到不了源站(§ 4.5 讲过),没命中的请求回源时,源站前面照样站着 LB。
这里顺带交代一下负载均衡在机房里的位置关系:它通常站在防火墙(回指 § 5.4)的后面。防火墙管"谁能进"——先把不怀好意的流量挡在门外;负载均衡管"进来了去哪"——把合法的请求分给后端的服务器。要是顺序反了,让 LB 直接暴露在公网入口,攻击流量会把它的算力耗尽,合法的用户反而被挤出去。标准架构是:公网 → 防火墙 → 负载均衡 → 应用服务器。这也正好串起第 5 章的四个角色:防火墙管门、负载均衡管分配、交换机管局域网内、路由器管跨网。
第三站:你家。坦白讲,家用场景基本用不到负载均衡。家里一般就一台出口路由器(§ 5.2 讲过那台"五合一"盒子),后面没有"一堆服务器"要分派。唯一沾点边的:双 WAN 路由器(同时接两条宽带,比如一条电信一条移动,让不同设备分流出口)——那是"出口分流",算家用版的最简负载均衡,但绝大多数家庭用不上。换句话说:负载均衡是"服务器多了之后必然出现的角色"——只要你有一台以上对外提供相同服务的机器,就总得有个东西站在前面分派。
还有一个值得知道的工程细节:负载均衡器自己也会成为瓶颈,所以生产环境通常是"两台 LB 互备"。一台挂了,另一台通过"虚拟 IP 漂移"接管同一个地址(这就是术语表里那个"故障转移 Failover")。就好比奶茶店门口摆了两个分单员,一个中暑了,另一个立刻接替他喊号。
常见误区:关于负载均衡的五个误解
负载均衡是"听起来很简单、用起来全是坑"的典型。五个流传最广的误解,一次拆干净——每一个都能用这一节前面讲过的原理回答。
- 误解一:"负载均衡器就是一台超强服务器"恰恰相反,它自己很轻。它不干重活、不处理业务,它的全部价值是调度。换成大白话:分单员自己不做奶茶,你把所有原料堆给他,他也变不出奶茶来。加一台 LB 不等于加算力。
- 误解二:"负载均衡会检查每个包的内容"默认不检查。四层 LB 跟路由器一样"只认地址不拆包"(回指 § 5.2:分拣中心只认面单、不拆包裹);只有七层 LB 才拆开 HTTP 看内容,而且这也是它的配置选项,不是默认行为。
- 误解三:"有负载均衡就不会宕机了"LB 自己也会挂,所以生产环境是两台互备(VIP 漂移)。而且健康检查有延迟——探测是"每隔几秒"一次,后端刚挂的那几秒里,仍可能被派到几个请求。负载均衡不消灭故障,它把故障的影响面从"全站不可用"缩小到"少数几个请求失败"。
- 误解四:"轮询最公平"请求重量不均时,轮询反而制造不公平——重请求扎堆到同一台机器。好比食堂窗口挨个叫号,可要是有人一次打十份饭,队伍照样乱。最少连接(看忙闲)才是这类场景下更"公平"的算法。
- 误解五:"加了负载均衡,网站就快了"LB 不加带宽、不加算力,它只是让已有的资源用得更均匀。单台服务器本身就慢(比如数据库查询要 2 秒),LB 也救不了——水管一共多粗是机房和运营商的事(回指 § 1.6 带宽 vs 时延),分单员只能保证水龙头不闲着。
五个误解背后其实是一个共同点:把"调度"这个角色的能力,安到了"算力"或"带宽"头上。分清"谁负责什么",误解自然不攻自破。
最后,把第 5 章的五个角色摆在一起收官。这一节是第 5 章"网络设备"的最后一节,五个角色正好拼出一个数据包从你家到网站的完整故事:
- 交换机(§ 5.1)同一条街的收发室,认 MAC,把帧送到该送的人——管"局域网内"。
- 路由器(§ 5.2)城际分拣中心,认 IP、查路由表,一站一站接力——管"跨网络"。
- 网关与光猫(§ 5.3)家门口那道门 + 光信号与电信号的翻译官——管"出家"。
- 防火墙(§ 5.4)门卫,按规则决定谁能进、谁不能进——管"安全"。
- 负载均衡(§ 5.5)进门后的大堂经理,把客人分给不同的窗口——管"分配"。
五个人各管一段、互不抢活——这本身就是 § 2.1/§ 2.2 分层思想的胜利:每一层只解决自己的问题,合起来解决整件事。下一章开始,网络篇将剪断网线进入无线世界:WiFi、蓝牙、4G/5G、卫星互联网——那是第 6 章《无线与移动》的主角。
一句话总结:负载均衡器是"奶茶店门口的分单员"——它站在一群后端服务器前面,对外只亮一个门面(虚拟 IP),对内按一套规则把请求分给不同的服务器,并定期确认每台后端还活着。谁也别闲着、谁也别累趴。
机制上记住五件事:
· 为什么需要它:一台服务器扛不住海量请求(回指 § 4.5),水平扩展出多台服务器之后,总得有个"分单员"站在前面。
· 四层 vs 七层:L4 只看 IP+端口(叫号机只看号),L7 拆开 HTTP 看内容(分诊台听你要办什么事)。HTTP 本身的规范是 RFC 9110。
· 五种分发算法:轮询(挨个叫号)、加权轮询(老手多接)、最少连接(谁闲给谁)、IP 哈希(同一人总去同一窗口)、一致性哈希(加减机器影响最小)。其中一致性哈希是工程实践概念,没有对应的 RFC。
· 健康检查:主动探测(LB 定时发请求问"你还活着吗")或被动心跳(后端自己举手报到),挂了就摘除、修好再放回。
· 会话保持:sticky session 把同一用户永远粘到同一台(有单点风险);现代做法是共享会话存储,或干脆无状态、把状态写进 Cookie(回指 § 4.5)。
第 5 章到此收官:交换机(§ 5.1)管同一条街、路由器(§ 5.2)管城际干线、网关与光猫(§ 5.3)管家门口那道门、防火墙(§ 5.4)管谁能进门、负载均衡(§ 5.5)管进门之后去哪个窗口——五个角色各管一段,互不抢活。
下一步:网络篇要进入无线世界了——下一章《无线与移动》讲 WiFi、蓝牙、4G/5G 和卫星互联网;第一节《WiFi 原理》已经上线,点击右下角就能接上:那是切断电缆后的自由。