什么是LVS?
LVS(Linux Virtual Server,Linux 虚拟服务器)是一个基于 Linux 内核实现的四层负载均衡解决方案,由章文嵩博士于 1998 年创建,现已集成到 Linux 主线内核中。
| 术语 | 说明 |
|---|---|
| Director(调度器) | 负载均衡器,接收客户端请求并按调度算法转发给后端 RS |
| Real Server(RS) | 后端真实服务器,实际处理请求 |
| VIP(Virtual IP) | 虚拟 IP,客户端访问的统一入口地址 |
| DIP(Director IP) | Director 与 RS 通信使用的真实 IP |
| RIP(Real Server IP) | RS 的真实 IP 地址 |
LVS 工作在 OSI 模型的第四层(传输层),基于 TCP/UDP 进行请求转发。相比七层负载均衡(Nginx、HAProxy),LVS 具有以下特点:
-
高性能:工作在内核态,直接处理网络包,吞吐量极高
-
低延迟:无需解析应用层协议,转发效率接近线速
-
透明性:对客户端完全透明,客户端只感知 VIP
-
可扩展:支持大规模集群,单台 Director 可承载数万并发连接
应用场景
-
高并发 Web 服务集群
-
数据库读写分离(MySQL Proxy)
-
DNS 服务器集群
-
邮件服务器集群
-
VS/NAT模式(Virtual Server via Network Address Translation)

-
Director 将请求报文用 IPIP 隧道封装,通过新 IP 头转发给 RS
-
RS 解封装后处理请求,直接回复客户端
特点:
-
支持跨网段、跨机房部署(RS 可以分布在不同物理位置)
-
响应不经过 Director,性能较好
-
需要 RS 支持 IPIP 隧道协议
-
VS/DR模式(Direct Routing)

工作原理:
-
Director 只改写请求报文的目标 MAC 地址(不修改 IP)
-
RS 配置 lo:0 虚拟接口绑定 VIP
-
RS 直接回复客户端(响应不经过 Director)
特点:
-
性能最高(响应流量不经过 Director)
-
要求 Director 和 RS 在同一二层网络
-
RS 必须配置 ARP 抑制(避免 RS 直接响应 ARP 请求)
-
不支持端口映射
VS/TUN模式(IP Tunneling)

-
Director 将请求报文用 IPIP 隧道封装,通过新 IP 头转发给 RS
-
RS 解封装后处理请求,直接回复客户端
特点:
-
支持跨网段、跨机房部署(RS 可以分布在不同物理位置)
-
响应不经过 Director,性能较好
-
需要 RS 支持 IPIP 隧道协议
-
VS/FULLNAT模式(Full Network Address Translation)

-
同时修改请求的源 IP 和目标 IP
-
响应同样经过 Director 做反向转换
特点:
-
不要求 RS 配置 VIP 或 ARP 抑制
-
RS 可以跨二层网络部署
-
支持端口映射
-
性能最差(双向都经过 Director,且需要完整的 NAT 转换)
-
| VS/NAT | VS/DR | VS/TUN | VS/FULLNAT | |
|---|---|---|---|---|
| 修改目标 | IP+端口 | MAC 地址 | IPIP 封装 | 源+目标 IP |
| 响应路径 | 经 Director | 直接响应 | 直接响应 | 经 Director |
| 性能 | 中 | 最高 | 高 | 低 |
| RS 网络要求 | 私有 IP,网关指向 Director | 同二层网络 | 跨网段/跨机房 | 任意 |
| ARP 抑制 | 不需要 | 必须 | 不需要 | 不需要 |
| 端口映射 | 支持 | 不支持 | 不支持 | 支持 |
| 配置复杂度 | 简单 | 中等 | 复杂 | 复杂 |
| 适用规模 | 小规模 | 大规模 | 分布式部署 |
静态调度和动态调度

| 缩写 | 说明 | |
|---|---|---|
| Round Robin | rr | 轮询,请求依次分配给每台 RS,循环往复 |
| Weighted Round Robin | wrr | 加权轮询,根据权重分配,权重高的 RS 获得更多请求 |
| Destination Hashing | dh | 目标地址哈希,相同目标 IP 的请求始终发往同一台 RS |
| Source Hashing | sh | 源地址哈希,相同源 IP 的请求始终发往同一台 RS(会话保持) |

| 算法 | 缩写 | 说明 |
|---|---|---|
| Least Connections | lc | 最小连接数,分配给当前活跃连接数最少的 RS |
| Weighted Least Connections | wlc | 加权最小连接数,结合权重和连接数计算(默认算法) |
| Locality-Based Least Connections | lblc | 基于局部性的最小连接,针对目标 IP 的负载均衡 |
| Locality-Based Least Connections with Replication | lblcr | 带复制的 lblc,支持 RS 复制 |
| Shortest Expected Delay | sed | 最短期望延迟,(当前连接数+1)/权重 最小的 RS |
| Never Queue | nq | 永不排队,有空闲 RS 直接分配,无需等待 |
| 场景 | 推荐算法 | 原因 |
|---|---|---|
| 静态内容 Web 集群 | rr / wrr | 请求处理时间相近,轮询即可 |
| 动态内容(PHP/Java) | lc / wlc | 请求处理时间差异大,按连接数分配更均衡 |
| 需要会话保持 | sh | 同源 IP 始终到同一台 RS |
| 长连接服务(WebSocket) | lc | 连接数反映真实负载 |
| 短连接高并发 | rr | 开销最小,吞吐量最高 |
LVS 工作原理
LVS 的核心是 Linux 内核中的 IPVS(IP Virtual Server) 模块,它工作在 Netfilter 框架的 INPUT/PREROUTING 钩子点。

Virtual Server (VIP:Port)
├── Scheduler (调度算法,如 rr)
└── Real Server List
├── RS1 (RIP:Port, 权重, 模式)
│ ├── ActiveConn (当前活跃连接数)
│ └── InActConn (非活跃连接数)
└── RS2 (RIP:Port, 权重, 模式)
├── ActiveConn
└── InActConn
LVS 维护一个连接跟踪表,记录每个连接的状态:
# 查看连接跟踪表
ipvsadm -L -n -c
-
NONE:初始状态
-
ESTABLISHED:连接已建立
-
SYN_RECV:收到 SYN 包
-
FIN_WAIT:等待 FIN
-
TIME_WAIT


这是 LVS 最核心的问题:Director 收到目标为 VIP 的请求后,怎么知道该转发给哪台 RS?
Client 请求到达 Director
↓
① IPVS 规则匹配
Director 检查数据包的目标 IP:Port
匹配虚拟服务表(VS Table)中的 VIP:Port 条目
↓
② 调度算法选择 RS
根据配置的调度算法(rr/lc/wrr 等),从 RS 列表中选一台
↓
③ 获取 RS 的真实 IP(RIP)
从 IPVS 的 RS 列表中找到该 RS 的 RIP
↓
④ 转发到 RS
根据工作模式(DR/NAT/TUN),将请求发往 RIP
虚拟服务哈希表,以 (协议, VIP, 端口)
// 内核中 IPVS 的虚拟服务查找逻辑(简化)
struct ip_vs_service *svc;
svc = ip_vs_service_get(af, protocol, vip, port);
// 返回匹配的虚拟服务条目,包含 RS 列表和调度算法
# 查看 IPVS 虚拟服务表(用户态)
ipvsadm -L -n
# 输出示例:
# IP Virtual Server version 1.2.1 (size=4096)
# Prot LocalAddress:Port Scheduler Flags
# -> RemoteAddress:Port Forward Weight ActiveConn InActConn
# TCP 10.211.55.100:80 rr
# -> 10.211.55.4:80 Route 1 0 0
# -> 10.211.55.5:80 Route 1 0 0
查找过程:
-
数据包到达 Director,目标 IP = 10.211.55.100:80
-
内核 IPVS 模块在哈希表中查找
TCP 10.211.55.100:80 -
命中虚拟服务条目,获取关联的 RS 链表和调度器
以 rr(轮询) 为例,内核中的选择逻辑:
// 简化版 rr 调度器
struct ip_vs_dest *rr_schedule(struct ip_vs_service *svc)
{
// 获取上次选择的 RS
struct ip_vs_dest *dest = svc->last_dest;
// 循环链表,选下一个 RS
dest = list_next_or_null(svc->dest_list, dest);
// 更新 last_dest 指针
svc->last_dest = dest;
// 返回 RS 的 RIP
return dest;
}
不同算法的选择依据:
| 算法 | 选择依据 | 内核查找方式 |
|---|---|---|
| rr | 轮询指针 | 链表顺序遍历,取下一个 |
| wrr | 权重计数器 | 每个 RS 有权重值,权重高的被选中的概率大 |
| lc | 活跃连接数 | 遍历 RS 链表,找 ActiveConn 最小的 |
| wlc | 连接数/权重 | 计算 (ActiveConn+1)/weight,取最小值 |
| sh | 源 IP 哈希 | hash(源IP) % RS数量,直接定位 |
| dh | 目标 IP 哈希 | hash(目标IP) % RS数量 |
Director 找到 RS 的 RIP 后:
① 不修改 IP 头(源 IP=Client,目标 IP=VIP)
② 通过 ARP 表获取 RS 的 MAC 地址
③ 只改写以太网帧的目标 MAC 地址为 RS 的 MAC
④ 将数据包从 Director 的物理网卡发出
Director 的 ARP 表:
10.211.55.4 → xx:xx:xx:xx:xx:04 (RS1 的 MAC)
10.211.55.5 → xx:xx:xx:xx:xx:05 (RS2 的 MAC)
为什么 RS 能收到目标 IP 不是自己的包?
-
-
内核收到数据包后,检查目标 IP = 10.211.55.100
-
匹配 lo:0 上的 VIP,认为这是给自己的包
-
Director 找到 RS 的 RIP 后:
① 修改 IP 头:目标 IP 从 VIP 改为 RIP
② 可选:修改目标端口(端口映射)
③ 重新计算 IP 校验和
④ 通过路由表转发给 RS
Director 的路由表:
10.211.55.0/24 dev enp0s5 → RS1/RS2 在同一网段
Director 找到 RS 的 RIP 后:
① 在原 IP 包外面包一层新 IP 头
② 外层源 IP = DIP,外层目标 IP = RIP
③ 通过路由发送出去
④ RS 收到后解封装,看到内层目标 IP = VIP
⑤ 匹配 lo:0 上的 VIP,交给应用处理
广告:













暂无评论内容