4. 负载均衡基础概念:L4/L7负载均衡、调度算法、会话保持
做HTTPS卸载,你得先搞懂负载均衡怎么干活。说白了一句话:怎么把流量分到后面的服务器上,分得又快又准。我做了这么多年,发现很多故障其实不是SSL卸载本身的问题,而是负载均衡策略没选对。这一节我们就把底层的三个核心概念说透。
4.1 L4 vs L7 负载均衡
负载均衡按网络层级分两类,这是个老生常谈的话题。但我见过不少团队把L4当L7用,或者反过来,坑得很。
| 层级 | 工作在哪一层 | 能看什么 | 典型产品 |
|---|---|---|---|
| L4 | 传输层(TCP/UDP) | 源/目的IP、端口 | LVS、F5 BIG-IP LTM |
| L7 | 应用层(HTTP/HTTPS) | URL、Header、Cookie、Body | Nginx、HAProxy、Envoy |
L4负载均衡,说白了只关心四元组。收到一个SYN包,看一眼目标IP:端口,挑一台后端就把包转发过去。速度快,性能高,但很傻——它不知道你是GET还是POST,也不知道访问的是/api还是/login。有一次客户抱怨502,我查了半天发现L4把长连接哈希到了不同后端,session全丢了。嗯,这就是L4的局限。
L7负载均衡,它得把TCP连接终结掉,把HTTP请求完整读出来,再根据URL或者其他应用层信息做转发。性能比L4低一些,但灵活得多。我个人习惯在做HTTPS卸载的场景里优先用L7,因为你只有到应用层才能拿到Session ID做会话保持,也才能处理请求重写、流量镜像这些高级功能。
4.2 调度算法
调度算法就是负载均衡器怎么挑后端服务器。不同的算法适合不同的场景,我一个个讲清楚。
轮询(Round Robin)
最简单,挨个发。适合所有后端性能一样、连接短、无状态的服务。我曾经在长连接场景用过轮询,结果连接集中在前面几台,因为轮询只关心新连接,不关心已有连接数。所以——短连接用轮询很好,长连接别用。
最少连接(Least Connections)
谁当前活跃连接少,就给谁。适合长连接、请求负载不均衡的场景。我有个项目跑WebSocket,后端机器有的快有的慢,用轮询导致慢的机器堆积了几千个连接,用最少连接立刻平衡了。不过要注意,如果后端处理能力差异很大,单纯看连接数不靠谱,得用加权版本。
源地址哈希(Source IP Hash)
对客户端IP做哈希,同一个IP永远落到同一台后端。好处是不需要额外存储会话信息,坏处是一台后端挂了,哈希表重新分配,大量会话会漂移。我做UDP游戏网关的时候用过,因为游戏要保留客户端状态,又不想引入Redis,源地址哈希凑合能用。
加权算法(Weighted Round Robin / Weighted Least Connections)
给每台机器设一个权重,性能强的权重高。这是最实用的方式。我习惯在HAProxy配置里先跑一段时间压力测试,根据CPU和内存占用算权重,而不是拍脑袋设。
下面这张图总结了常见算法的选择逻辑:
4.3 会话保持
会话保持,也叫黏性会话。意思就是同一个用户的所有请求,必须落到同一台后端服务器上。你想想看,用户登录后在A服务器写了session,下一次请求被负载均衡扔到了B服务器,B没这个session,用户就被踢回登录页了。这谁受得了?
会话保持主要有三种实现方式:
- 源IP保持:负载均衡根据客户端IP哈希,相同IP永远去同一台。最简单,但如果是NAT环境(比如公司内网上百人共享一个公网IP),所有流量都打到同一台,会跑偏。我自己在运营商项目里吃过这个亏。
- Cookie插入:负载均衡在第一次响应里插入一个Set-Cookie,里面包含后端服务器的标识。后续请求客户端带上这个Cookie,负载均衡就能直接定位。L7负载均衡最常用。我曾经在生产环境发现Cookie过期时间设太短,用户频繁重新分配,改了过期时间后问题解决。
- 应用层会话复制:多台后端共享一个会话存储(比如Redis),每台都能读到同一个会话。这已经不是单纯负载均衡的范畴了,而是架构设计。我建议只要条件允许,优先用集中式会话存储,配合无状态设计,这样负载均衡只需做简单的轮询,故障转移也干净。