一、 负载均衡详细介绍
1.1 四层负载均衡技术
1.1.1 经典四层负载均衡器 LVS 的相关术语
┌─────────────────────────────────────────────────────────────────┐
│ LVS核心术语详解 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 【VIP - Virtual IP】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 负载均衡器对外提供服务的虚拟IP地址 │ │
│ │ │ │
│ │ 示例: │ │
│ │ 1.2.3.4:80 → 承载网站www.example.com的访问 │ │
│ │ 1.2.3.4:443 → 承载HTTPS网站访问 │ │
│ │ │ │
│ │ 特点: │ │
│ │ • 客户端直接访问的地址 │ │
│ │ • 通常是公网IP │ │
│ │ • 可以在多个Director间漂移(高可用) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【DIP - Director IP】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Director(负载均衡器)自身的IP地址 │ │
│ │ │ │
│ │ 示例: │ │
│ │ 10.0.1.10 → Director服务器的管理IP │ │
│ │ │ │
│ │ 用途: │ │
│ │ • 与后端RS通信的源地址(NAT模式) │ │
│ │ • 与上游设备通信的地址 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【RIP - Real Server IP】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 后端真实服务器的IP地址 │ │
│ │ │ │
│ │ 示例: │ │
│ │ 192.168.1.101 → 后端服务器1 │ │
│ │ 192.168.1.102 → 后端服务器2 │ │
│ │ 192.168.1.103 → 后端服务器3 │ │
│ │ │ │
│ │ 特点: │ │
│ │ • 通常是私网IP │ │
│ │ • 对客户端不可见 │ │
│ │ • 处理实际业务请求 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【CIP - Client IP】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 发起请求的客户端的真实IP地址 │ │
│ │ │ │
│ │ 重要性: │ │
│ │ • 后端应用需要获取真实客户端IP │ │
│ │ • 日志记录、统计分析、安全防护需要 │ │
│ │ • 通过X-Forwarded-For或TOA获取 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【LIP - Local IP (FullNAT专用)】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ FullNAT模式下的内网转换IP地址 │ │
│ │ │ │
│ │ 示例: │ │
│ │ LIP池:192.168.100.200-192.168.100.250 │ │
│ │ │ │
│ │ 作用: │ │
│ │ • 替代CIP作为与RS通信的源地址 │ │
│ │ • 解决NAT模式RS网关必须指向LB的问题 │ │
│ │ • 支持跨网段部署 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 地址关系图: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [客户端] │ │
│ │ │ │ │
│ │ │ CIP:54321 → VIP:80 │ │
│ │ ↓ │ │
│ │ [Director] │ │
│ │ │ │ │
│ │ │ DIP:Port → RIP:Port(转发时) │ │
│ │ ↓ │ │
│ │ [Real Server] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.1.2 LVS 转发模式及配置实例
LVS 支持四种转发模式:DR、TUN、NAT、FullNAT。每种模式都有其独特的适用场景。
DR 模式(Direct Routing)
┌─────────────────────────────────────────────────────────────────┐
│ LVS-DR模式详解 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 工作原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 请求阶段: │ │
│ │ Client → [CIP→VIP] → Director │ │
│ │ 修改目标MAC → DIP MAC │ │
│ │ 源IP/目标IP保持不变! │ │
│ │ ↓ │ │
│ │ [RS] │ │
│ │ │ │
│ │ 响应阶段: │ │
│ │ [RS] │ │
│ │ ↑ │ │
│ │ [RIP→CIP] 直接发送 │ │
│ │ 完全绕过Director! │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✓ 性能最高(无NAT开销) │ │
│ │ ✓ 响应直接返回,不经过Director │ │
│ │ ✓ 适合大多数场景 │ │
│ │ ✗ RS必须与Director同网段 │ │
│ │ ✗ 需要ARP抑制配置 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置实例: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # Director配置 │ │
│ │ │ │
│ │ # 添加VIP │ │
│ │ ifconfig eth0:0 1.2.3.4/24 up │ │
│ │ │ │
│ │ # 添加虚拟服务(DR模式 -g) │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s rr -g │ │
│ │ │ │
│ │ # 添加后端服务器 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 192.168.1.101 -g │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 192.168.1.102 -g │ │
│ │ │ │
│ │ # RS配置 │ │
│ │ │ │
│ │ # 绑定VIP到lo接口 │ │
│ │ ifconfig lo:0 1.2.3.4/32 up │ │
│ │ │ │
│ │ # ARP抑制配置 │ │
│ │ echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore │ │
│ │ echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce │ │
│ │ echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore │ │
│ │ echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ARP抑制详解: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ arp_ignore参数: │ │
│ │ • 0: 响应任何网卡的ARP请求(默认) │ │
│ │ • 1: 只响应本地接口上的ARP请求 │ │
│ │ • 2: 只响应目标IP匹配本地接口的ARP请求 │ │
│ │ │ │
│ │ arp_announce参数: │ │
│ │ • 0: 使用任何接口的IP发送ARP(默认,可能暴露VIP) │ │
│ │ • 1: 尽量使用与目标IP同网段的接口发送 │ │
│ │ • 2: 只使用与目标IP精确匹配的接口发送 │ │
│ │ │ │
│ │ 推荐配置(RS上): │ │
│ │ arp_ignore = 1 │ │
│ │ arp_announce = 2 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
LVS-DR模式通过让Director仅修改数据包的目标MAC地址(保持IP不变)将请求转发给后端,而后端服务器必须将VIP绑定在回环接口(lo)上,这是因为物理网卡通常只能接收目标IP为自身真实IP的数据包,绑定lo相当于给内核协议栈开了一个"后门",让它能识别并接收目标为VIP的数据包,同时配合ARP抑制机制隐藏自身的VIP身份,确保请求只进不出;最终,后端服务器利用这个lo接口上的VIP作为源地址,将响应数据直接返回给客户端,完全绕过Director,从而实现了极高的吞吐量。
TUN 模式(IP Tunneling)
┌─────────────────────────────────────────────────────────────────┐
│ LVS-TUN模式详解 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 工作原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 请求阶段: │ │
│ │ │ │
│ │ Client → [内层: CIP→VIP] │ │
│ │ [外层: DIP→RIP] IPIP封装 │ │
│ │ ↓ │ │
│ │ [RS] │ │
│ │ │ │
│ │ RS内核自动解封装 │ │
│ │ 处理原始[CIP→VIP]请求 │ │
│ │ │ │
│ │ 响应阶段: │ │
│ │ [VIP→CIP] 直接发送 → Client │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✓ 支持跨网段部署(可跨越不同机房) │ │
│ │ ✓ 性能较高 │ │
│ │ ✓ 响应直接返回 │ │
│ │ ✗ RS内核需要支持IPIP隧道 │ │
│ │ ✗ 配置相对复杂 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置实例: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # Director配置 │ │
│ │ │ │
│ │ # 加载ip_vs_ipip模块 │ │
│ │ modprobe ip_vs_ipip │ │
│ │ │ │
│ │ # 添加VIP │ │
│ │ ifconfig tunl0 1.2.3.4/24 up │ │
│ │ │ │
│ │ # 添加虚拟服务(TUN模式 -i) │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s rr -i │ │
│ │ │ │
│ │ # 添加后端服务器 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 172.16.1.101 -i │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 172.16.1.102 -i │ │
│ │ │ │
│ │ # RS配置 │ │
│ │ │ │
│ │ # 绑定VIP到tunnel接口 │ │
│ │ ifconfig tunl0 1.2.3.4/24 up │ │
│ │ │ │
│ │ # ARP抑制(同DR模式) │ │
│ │ echo 1 > /proc/sys/net/ipv4/conf/tunl0/arp_ignore │ │
│ │ echo 2 > /proc/sys/net/ipv4/conf/tunl0/arp_announce │ │
│ │ │ │
│ │ # 关闭隧道端口转发 │ │
│ │ echo 1 > /proc/sys/net/ipv4/conf/tunl0/disable_policy │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
在LVS的TUN模式下,整个数据流转过程就像是一场精密的"套娃"接力赛。当客户端的请求到达Director(负载均衡器)后,内核中的IPVS模块首先会像门卫一样拦截并检查数据包的目标IP和端口,确认它是否属于预先配置好的集群服务,只有验证通过,Director才会启动转发逻辑;紧接着,Director并不会像NAT模式那样修改原始数据,而是采用IP隧道技术,在原始数据包(源CIP -> 目标VIP)的外面,再封装一层新的IP头(源DIP -> 目标RIP),这就像把一封写好的信(原始包)装进了一个新的快递箱(外层IP)里,快递箱上只写着从Director到后端服务器(RS)的地址,中间的路由器只看外层快递箱的地址(RIP)进行投递,完全看不到里面的信件内容(VIP),从而实现了跨越不同网段的传输;当后端服务器(RS)收到这个包后,网卡发现协议类型是IPIP(隧道协议),于是内核自动进行解封装,拆掉外层快递箱,露出了里面的原始信件;此时,内核检查原始信件的目标地址是VIP,为了接收这个包,RS必须在Loopback(lo)接口上绑定这个VIP(这与DR模式原理一致,为了不占用物理网卡IP且避免ARP冲突),一旦内核发现lo接口上有这个VIP,就会接收并处理该请求;处理完毕后,RS构建响应包,源IP是VIP(假装自己是Director),目标IP是CIP(客户端),然后直接通过eth0物理网卡发送给客户端,完全绕过了Director,从而实现了高性能的负载均衡。
NAT 模式
┌─────────────────────────────────────────────────────────────────┐
│ LVS-NAT模式详解 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 工作原理:
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 请求阶段(Inbound): │ │
│ │ Client → Director → RS │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 源IP: CIP (不变) │ │ │
│ │ │ 目标IP: VIP → RIP (仅修改目标IP) │ │ │
│ │ │ 源MAC: Client MAC → Director MAC │ │ │
│ │ │ 目标MAC: Director MAC → RS MAC │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 关键点:RS的网关必须指向Director! │ │
│ │ │ │
│ │ 响应阶段(Outbound): │ │
│ │ RS → Director → Client │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 因为RS的网关是Director │ │ │
│ │ │ RS的响应包会自动路由回Director │ │ │
│ │ │ 源IP: RIP → VIP (修改源IP!) │ │ │
│ │ │ 目标IP: DIP → CIP (恢复客户端IP) │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ 特点: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✓ 配置简单,易于理解 │ │
│ │ ✓ 支持端口映射 │ │
│ │ ✓ RS无需特殊配置 │ │
│ │ ✗ Director性能开销大(双向NAT) │ │
│ │ ✗ RS网关必须指向Director │ │
│ │ ✗ 扩展性受限 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置实例: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # Director配置 │ │
│ │ │ │
│ │ # 开启IP转发 │ │
│ │ echo 1 > /proc/sys/net/ipv4/ip_forward │ │
│ │ │ │
│ │ # 添加VIP(公网网卡) │ │
│ │ ifconfig eth0:0 1.2.3.4/24 up │ │
│ │ │ │
│ │ # 添加虚拟服务(NAT模式 -m) │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s rr -m │ │
│ │ │ │
│ │ # 添加后端服务器 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 192.168.1.101 -m │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 192.168.1.102 -m │ │
│ │ │ │
│ │ # RS配置(网关指向Director) │ │
│ │ route add default gw 192.168.1.10 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
LVS的NAT模式就像一个"全权代理的中转站",请求和响应都必须经过负载均衡器。当客户端请求到达时,负载均衡器将数据包的目标IP从虚拟IP(VIP)修改为后端真实服务器的IP(RIP)并转发;关键在于,为了让处理完的响应能回到负载均衡器,后端服务器的默认网关必须配置为负载均衡器的内网IP(DIP),这样响应包才会发回给负载均衡器,由它把源IP改回VIP后,最终返回给客户端。
FullNAT 模式(全地址转换)
FullNAT 模式是阿里巴巴开源的 LVS 增强版本,通过引入 LIP(Local IP)实现了完全透明的代理,彻底解耦了 Director 与后端 RS 的网络拓扑关系。
┌─────────────────────────────────────────────────────────────────┐
│ LVS-FullNAT模式详解 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 工作原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 核心创新:引入LIP(Local IP) │ │
│ │ │ │
│ │ 请求阶段: │ │
│ │ Client → [CIP→VIP] → Director │ │
│ │ SNAT: CIP→LIP(源地址转换) │ │
│ │ DNAT: VIP→RIP(目标地址转换) │ │
│ │ ↓ │ │
│ │ [RS] │ │
│ │ RS看到: LIP→RIP的请求 │ │
│ │ │ │
│ │ 响应阶段: │ │
│ │ [RS] │ │
│ │ ↑ │ │
│ │ 源: RIP → 目标: LIP │ │
│ │ RS响应给LIP(Director的地址) │ │
│ │ Director收到后再次转换: │ │
│ │ SNAT: RIP→VIP │ │
│ │ DNAT: LIP→CIP │ │
│ │ → Client │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 与NAT模式的区别: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ NAT模式: │ │
│ │ • 源地址被改为DIP(Director自己的IP) │ │
│ │ • DIP通常是公网IP,数量有限 │ │
│ │ • RS网关必须指向Director │ │
│ │ │ │
│ │ FullNAT模式: │ │
│ │ • 源地址被改为LIP(DIP的扩展/补充) │ │
│ │ • LIP可以是私网IP池,灵活扩展 │ │
│ │ • RS网关指向自己的网段网关,无需指向Director │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ LIP的作用与价值: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 为什么要用LIP而不是直接用DIP? │ │
│ │ │ │
│ │ 场景:Director有1个DIP,但需要连接10000个后端RS │ │
│ │ │ │
│ │ NAT模式问题: │ │
│ │ • 1个DIP的端口范围是0-65535 │ │
│ │ • 如果每RS占用1个端口,最多服务65535个RS │ │
│ │ • 实际上还有端口复用限制,更少 │ │
│ │ │ │
│ │ FullNAT + LIP解决方案: │ │
│ │ • LIP是一个IP池(如192.168.100.200-250,共51个) │ │
│ │ • 每个LIP仍可服务65535个连接 │ │
│ │ • 51个LIP × 65535 ≈ 330万连接 │ │
│ │ • 轻松支持大规模RS集群 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 跨网段部署的奥秘: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ FullNAT能跨网段部署的关键: │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ LIP网段 ←──三层路由可达──→ RIP网段 │ │ │
│ │ │ 192.168.100.x 172.16.1.x │ │ │
│ │ │ │ │ │
│ │ │ 只要路由器知道如何路由 │ │ │
│ │ │ LIP和RIP就能通信 │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 实际部署场景: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 场景1:同机房不同网段 │ │ │
│ │ │ • LIP: 192.168.100.200 │ │ │
│ │ │ • RIP: 192.168.101.101 │ │ │
│ │ │ • 通过核心交换机三层互联 │ │ │
│ │ ├─────────────────────────────────────────────────┤ │ │
│ │ │ 场景2:跨机房部署(北京→上海) │ │ │
│ │ │ • LIP: 192.168.100.200 │ │ │
│ │ │ • RIP: 172.16.101.101 │ │ │
│ │ │ • 通过专线/VPN跨地域互联 │ │ │
│ │ ├─────────────────────────────────────────────────┤ │ │
│ │ │ 场景3:云环境部署 │ │ │
│ │ │ • LIP: VPC内网IP │ │ │
│ │ │ • RIP: 其他可用区IP │ │ │
│ │ │ • 通过云厂商内网路由 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✓ 支持真正的跨网段、跨机房部署 │ │
│ │ ✓ RS网关无需指向Director │ │
│ │ ✓ Director和RS网络拓扑解耦 │ │
│ │ ✓ 支持大规模RS集群 │ │
│ │ ✓ 适合云环境和混合云部署 │ │
│ │ ✗ 需要LVS内核模块支持(aliyun/alibaba分支) │ │
│ │ ✗ 配置相对复杂 │ │
│ │ ✗ 性能略低于DR模式(双重地址转换) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置实例: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # Director配置 │ │
│ │ │ │
│ │ # 加载FullNAT模块(需要aliyun/alibaba内核) │ │
│ │ modprobe ip_vs_fullnat │ │
│ │ │ │
│ │ # 开启IP转发 │ │
│ │ echo 1 > /proc/sys/net/ipv4/ip_forward │ │
│ │ │ │
│ │ # 添加VIP │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s rr -b │ │
│ │ # 注意:-b 参数启用FullNAT模式 │ │
│ │ │ │
│ │ # 添加LIP池(内网IP池) │ │
│ │ ipvsadm --start-daemon master -t 1.2.3.4:80 \ │ │
│ │ --localip 192.168.100.200-192.168.100.250 │ │
│ │ │ │
│ │ # 添加后端服务器 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 172.16.1.101 -b │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 172.16.1.102 -b │ │
│ │ │ │
│ │ # RS配置(网关指向本机网段网关) │ │
│ │ route add default gw 172.16.1.1 │ │
│ │ # 注意:不再是Director的IP! │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
FullNAT 模式通过引入内网 IP(LIP),让负载均衡器(Director)在客户端和后端服务器(RS)之间扮演了一个完全透明的"全能代理人"。当客户端的请求到达 Director 的公网 VIP 时,Director 会立即执行"双重变脸":它不仅将目标地址从 VIP 修改为后端服务器的真实 IP(RIP),还会将源地址从客户端 IP(CIP)修改为自己的内网 IP(LIP),然后把这个"伪装"后的数据包发往内网;此时,RS 看到的请求就像是 Director 自己发来的,于是它把响应包直接发回给 LIP,Director 收到后再把源地址改回 VIP、目标地址改回 CIP,最终返回给客户端。
1.1.3 不同协议下的负载均衡
LVS 不仅支持常见的 HTTP 协议,还能处理 TCP、UDP 等多种协议的负载均衡:
┌─────────────────────────────────────────────────────────────────┐
│ LVS协议支持 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 【TCP协议】 │ │
│ │ │ │
│ │ 典型场景: │ │
│ │ • HTTP/HTTPS │ │
│ │ • SSH/Telnet │ │
│ │ • FTP/SFTP │ │
│ │ • SMTP/POP3/IMAP │ │
│ │ • 数据库连接(MySQL/PostgreSQL) │ │
│ │ │ │
│ │ 配置示例: │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s rr │ │
│ │ ipvsadm -A -t 1.2.3.4:443 -s wlc │ │
│ │ ipvsadm -A -t 1.2.3.4:3306 -s sh │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 【UDP协议】 │ │
│ │ │ │
│ │ 典型场景: │ │
│ │ • DNS │ │
│ │ • DHCP │ │
│ │ • VoIP/SIP │ │
│ │ • 游戏服务器 │ │
│ │ • 流媒体 │ │
│ │ │ │
│ │ 配置示例: │ │
│ │ ipvsadm -A -u 1.2.3.4:53 -s rr │ │
│ │ ipvsadm -A -u 1.2.3.4:5060 -s lc │ │
│ │ │ │
│ │ 注意:UDP无连接概念,使用统一调度 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 【防火墙实例(FW)】 │ │
│ │ │ │
│ │ 两臂模式,实现双向负载均衡: │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Client Director RS │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ │──CIP→VIP───→│ │ │ │ │
│ │ │ │ │──DIP→RIP────→│ │ │ │
│ │ │ │ │ │──RIP→DIP───→│ │ │
│ │ │ │←────VIP→CIP──│ │ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 配置: │ │
│ │ ipvsadm -A -f 1 -s rr │ │
│ │ ipvsadm -a -f 1 -r RIP1 -g │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.1.4 获取真实的客户端 IP 地址和端口信息
在后端服务器获取真实客户端 IP 是一个常见且重要的需求。
场景分析
┌─────────────────────────────────────────────────────────────────┐
│ 获取真实客户端IP的需求 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 问题: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 在NAT模式下: │ │
│ │ • RS看到的源IP是DIP/LIP,而非CIP │ │
│ │ • 应用日志记录的是错误的IP │ │
│ │ │ │
│ │ 在DR/TUN模式下: │ │
│ │ • RS可以直接看到CIP │ │
│ │ • 但某些情况下仍需特殊处理 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 解决方案: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 方案一:X-Forwarded-For头(Nginx/七层代理) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ X-Forwarded-For: CIP, CIP2, CIP3 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 方案二:TOA(TCP Option Address) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 在TCP选项中携带CIP信息 │ │ │
│ │ │ 四层负载均衡时保留客户端信息 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 方案三:Proxy Protocol │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ HAProxy协议,在连接前发送代理信息头 │ │ │
│ │ │ PROXY TCP4 CIP DIP LPORT RPORT\r\n │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
各模式下的实现方法
┌─────────────────────────────────────────────────────────────────┐
│ 各模式获取真实IP方案 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 【NAT模式】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ L4 LB使用LIP替代CIP,RS无法直接获取 │ │
│ │ │ │
│ │ 解决方案: │ │
│ │ 1. L4 LB在TCP选项中携带CIP(需要LB和RS都支持TOA) │ │
│ │ 2. L4 LB → L7代理 → RS(通过X-Forwarded-For) │ │
│ │ │ │
│ │ TOA配置示例: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ # L4 LB加载TOA模块 │ │ │
│ │ │ modprobe toa │ │ │
│ │ │ │ │ │
│ │ │ # RS加载TOA模块 │ │ │
│ │ │ modprobe toa │ │ │
│ │ │ │ │ │
│ │ │ # 应用读取CIP │ │ │
│ │ │ getsockopt(sock, SOL_IP, TCP_TOA, &toa_opt) │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【DR/TUN模式】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ RS可以直接看到CIP,无需特殊处理 │ │
│ │ 响应也直接从RS返回,不经过LB │ │
│ │ │ │
│ │ 注意: │ │
│ │ • 如果中间有NAT设备,需要额外处理 │ │
│ │ • 应用直接读取连接对端地址即可 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【L7代理模式(推荐)】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ L4 LB → Nginx → RS │ │
│ │ │ │
│ │ Nginx添加: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ proxy_set_header X-Real-IP $remote_addr; │ │ │
│ │ │ proxy_set_header X-Forwarded-For $proxy_add_x │ │ │
│ │ │ _forwarded_for; │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 应用读取: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ // 从HTTP Header获取 │ │ │
│ │ │ String realIP = request.getHeader("X-Real-IP"); │ │ │
│ │ │ // 或 │ │ │
│ │ │ String realIP = request.getHeader( │ │ │
│ │ │ "X-Forwarded-For"); │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 七层负载均衡技术
1.2.1 经典七层负载均衡器 Nginx 的部署架构
┌─────────────────────────────────────────────────────────────────┐
│ Nginx负载均衡部署架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 典型部署架构: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [用户请求] │ │
│ │ │ │ │
│ │ ↓ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ Nginx集群(入口) │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐│ │ │
│ │ │ │ Nginx 1 │ │ Nginx 2 │ │ Nginx 3 ││ ... │ │
│ │ │ └────┬────┘ └────┬────┘ └────┬────┘│ │ │
│ │ │ │ │ │ │ │ │
│ │ │ └───────────┴───────────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ Keepalived + VIP │ │ │
│ │ └──────────────────┬────────────────────┘ │ │
│ │ │ │ │
│ │ ↓ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ Nginx集群(内部) │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐│ │ │
│ │ │ │ 静态资源│ │ API代理 │ │ WebSocket││ │ │
│ │ │ └────┬────┘ └────┬────┘ └────┬────┘│ │ │
│ │ └────────┴───────────┴───────────┴────┘ │ │
│ │ │ │ │
│ │ ↓ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ 后端服务集群 │ │ │
│ │ │ │ │ │
│ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │
│ │ │ │ App1 │ │ App2 │ │ App3 │ │ App4 │ │ ... │ │
│ │ │ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Nginx配置示例: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ http { │ │
│ │ # 定义上游服务器组 │ │
│ │ upstream backend_servers { │ │
│ │ server 10.0.1.10:8080 weight=5; │ │
│ │ server 10.0.1.11:8080 weight=3; │ │
│ │ server 10.0.1.12:8080 backup; │ │
│ │ │ │
│ │ keepalive 1000; │ │
│ │ } │ │
│ │ │ │
│ │ server { │ │
│ │ listen 80; │ │
│ │ server_name www.example.com; │ │
│ │ │ │
│ │ # 静态资源 │ │
│ │ location /static/ { │ │
│ │ alias /var/www/static/; │ │
│ │ expires 7d; │ │
│ │ } │ │
│ │ │ │
│ │ # API代理 │ │
│ │ location /api/ { │ │
│ │ proxy_pass http://backend_servers; │ │
│ │ proxy_set_header Host $host; │ │
│ │ proxy_set_header X-Real-IP $remote_addr; │ │
│ │ } │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2.2 Nginx 转发粒度控制
Nginx 支持多种粒度的转发控制,从连接级到请求级:
┌─────────────────────────────────────────────────────────────────┐
│ Nginx转发粒度控制 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 【连接级转发(upstream)】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ upstream块级别的负载均衡: │ │
│ │ │ │
│ │ upstream backend { │ │
│ │ server 10.0.1.10:8080; │ │
│ │ server 10.0.1.11:8080; │ │
│ │ server 10.0.1.12:8080; │ │
│ │ } │ │
│ │ │ │
│ │ 所有到/upstream的请求都会进行负载均衡 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【location级转发】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 基于URL路径的精细路由: │ │
│ │ │ │
│ │ location /api/users/ { │ │
│ │ proxy_pass http://user_servers; │ │
│ │ } │ │
│ │ │ │
│ │ location /api/orders/ { │ │
│ │ proxy_pass http://order_servers; │ │
│ │ } │ │
│ │ │ │
│ │ location /api/payments/ { │ │
│ │ proxy_pass http://payment_servers; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【Header级转发】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 基于请求头的智能路由: │ │
│ │ │ │
│ │ # 基于版本header路由 │ │
│ │ location /api/ { │ │
│ │ if ($http_x_api_version = "v2") { │ │
│ │ proxy_pass http://backend_v2; │ │
│ │ } │ │
│ │ if ($http_x_api_version = "v3") { │ │
│ │ proxy_pass http://backend_v3; │ │
│ │ } │ │
│ │ proxy_pass http://backend_default; │ │
│ │ } │ │
│ │ │ │
│ │ # 基于Cookie路由 │ │
│ │ location / { │ │
│ │ if ($cookie_user_tier = "premium") { │ │
│ │ proxy_pass http://premium_servers; │ │
│ │ } │ │
│ │ proxy_pass http://standard_servers; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【动静分离】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # 静态资源 - 直接服务或CDN │ │
│ │ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff) ${ │ │
│ │ expires 30d; │ │
│ │ add_header Cache-Control public; │ │
│ │ } │ │
│ │ │ │
│ │ # HTML页面 │ │
│ │ location ~* \.html$ { │ │
│ │ proxy_pass http://backend_servers; │ │
│ │ } │ │
│ │ │ │
│ │ # API请求 │ │
│ │ location /api/ { │ │
│ │ proxy_pass http://api_servers; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2.3 获取真实的客户端 IP 地址和端口信息
Nginx 通过特定 header 传递真实客户端信息:
┌─────────────────────────────────────────────────────────────────┐
│ Nginx传递客户端IP机制 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 【X-Real-IP】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 配置: │ │
│ │ proxy_set_header X-Real-IP $remote_addr; │ │
│ │ │ │
│ │ 传递内容: │ │
│ │ X-Real-IP: 203.0.113.50(真实客户端IP) │ │
│ │ │ │
│ │ 使用场景: │ │
│ │ • 简单直接 │ │
│ │ • 只有一个代理层时 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【X-Forwarded-For】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 配置: │ │
│ │ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; │ │
│ │ │ │
│ │ 传递内容: │ │
│ │ X-Forwarded-For: 203.0.113.50, 198.51.100.178 │ │
│ │ 第一个IP 第二个IP │ │
│ │ (原始客户端) (上一跳) │ │
│ │ │ │
│ │ 解析方式: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ # 获取第一个IP(原始客户端) │ │ │
│ │ │ real_ip = xff.split(',')[0].strip() │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【X-Forwarded-Port / X-Forwarded-Proto】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 配置: │ │
│ │ proxy_set_header X-Forwarded-Port $server_port; │ │
│ │ proxy_set_header X-Forwarded-Proto $scheme; │ │
│ │ │ │
│ │ 传递内容: │ │
│ │ X-Forwarded-Port: 443 │ │
│ │ X-Forwarded-Proto: https │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 完整配置示例: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ location / { │ │
│ │ proxy_pass http://backend; │ │
│ │ │ │
│ │ # 保留原始Host │ │
│ │ proxy_set_header Host $host; │ │
│ │ │ │
│ │ # 传递客户端IP │ │
│ │ proxy_set_header X-Real-IP $remote_addr; │ │
│ │ proxy_set_header X-Forwarded-For │ │
│ │ $proxy_add_x_forwarded_for; │ │
│ │ │ │
│ │ # 传递协议和端口 │ │
│ │ proxy_set_header X-Forwarded-Proto $scheme; │ │
│ │ proxy_set_header X-Forwarded-Port $server_port; │ │
│ │ │ │
│ │ # 传递连接信息 │ │
│ │ proxy_set_header Connection ""; │ │
│ │ │ │
│ │ # 超时配置 │ │
│ │ proxy_connect_timeout 60s; │ │
│ │ proxy_read_timeout 60s; │ │
│ │ proxy_send_timeout 60s; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.3 Real Server 调度算法
调度算法决定了如何将客户端请求分配到后端服务器,是负载均衡的核心。
1.3.1 轮询(RR)
轮询算法将请求依次分配给每个服务器,循环往复。
┌─────────────────────────────────────────────────────────────────┐
│ 轮询调度算法(RR) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 算法原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 请求序列: R1 R2 R3 R4 R5 R6 R7 R8 │ │
│ │ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ RS1 → RS2 → RS3 → RS1 → RS2 → RS3 → RS1 →...│ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 规则:第N个请求 → 第(N mod 3)个服务器 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • 服务器性能一致 │ │
│ │ • 无状态服务 │ │
│ │ • 简单高效 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置:ipvsadm -A -t 1.2.3.4:80 -s rr │
│ │
└─────────────────────────────────────────────────────────────────┘
1.3.2 加权轮询(WRR)
加权轮询根据服务器权重比例分配请求。
┌─────────────────────────────────────────────────────────────────┐
│ 加权轮询调度算法(WRR) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 算法原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 服务器权重: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ RS1: weight=5 │ │ │
│ │ │ RS2: weight=3 │ │ │
│ │ │ RS3: weight=2 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 分配模式: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 周期内共5+3+2=10个请求 │ │ │
│ │ │ │ │ │
│ │ │ RS1: 5次 ████████████████ │ │ │
│ │ │ RS2: 3次 ████████████ │ │ │
│ │ │ RS3: 2次 ████████ │ │ │
│ │ │ │ │ │
│ │ │ 请求序列: R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 │ │ │
│ │ │ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ │ │ │
│ │ │ RS1: 1 2 3 4 5 │ │ │
│ │ │ RS2: 1 2 3 │ │ │
│ │ │ RS3: 1 2 │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • 服务器性能不一致(CPU/内存差异) │ │
│ │ • 需要灰度发布(低权重先验证) │ │
│ │ • 资源调配优化 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s wrr │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 10.0.1.10 -w 5 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 10.0.1.11 -w 3 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 10.0.1.12 -w 2 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.3.3 最少连接调度(LC)
最少连接算法将请求分配给当前连接数最少的服务器。
┌─────────────────────────────────────────────────────────────────┐
│ 最少连接调度算法(LC) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 算法原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 选择当前活动连接数最少的服务器: │ │
│ │ │ │
│ │ 当前连接状态: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ RS1: 50个活动连接 │ │ │
│ │ │ RS2: 30个活动连接 ← 最少,选择! │ │ │
│ │ │ RS3: 45个活动连接 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 第11个请求 → RS2 │ │
│ │ │ │
│ │ 更新后: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ RS1: 50个活动连接 │ │ │
│ │ │ RS2: 31个活动连接 (+1) │ │ │
│ │ │ RS3: 45个活动连接 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • 长连接服务(数据库、游戏服务器) │ │
│ │ • 请求处理时间差异大的服务 │ │
│ │ • 会话保持需求 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 公式:选择活动连接数最少的服务器 │
│ │
│ 配置:ipvsadm -A -t 1.2.3.4:80 -s lc │
│ │
└─────────────────────────────────────────────────────────────────┘
1.3.4 加权最少连接调度(WLC)
加权最少连接是最常用的调度算法,综合考虑权重和当前负载。
┌─────────────────────────────────────────────────────────────────┐
│ 加权最少连接调度算法(WLC) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 算法原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 选择负载最轻的服务器: │ │
│ │ │ │
│ │ 公式: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ 负载值 = (活动连接数 × 256 + 非活动连接数) │ │ │
│ │ │ ÷ 权重 │ │ │
│ │ │ │ │ │
│ │ │ 选择负载值最低的服务器 │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 计算示例: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 服务器状态: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ RS1: weight=5, 活跃=10, 非活跃=2 │ │ │
│ │ │ RS2: weight=3, 活跃=5, 非活跃=1 │ │ │
│ │ │ RS3: weight=2, 活跃=3, 非活跃=0 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 计算负载值: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ RS1: (10×256 + 2) ÷ 5 = 512.4 │ │ │
│ │ │ RS2: (5×256 + 1) ÷ 3 = 427.0 ← 最低,选择! │ │ │
│ │ │ RS3: (3×256 + 0) ÷ 2 = 384.0 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 虽然RS3非活跃连接最少,但综合考虑后RS2负载最轻 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • 通用场景首选 │ │
│ │ • 性能差异 + 动态负载 │ │
│ │ • 生产环境推荐 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s wlc │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 10.0.1.10 -w 5 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 10.0.1.11 -w 3 │ │
│ │ ipvsadm -a -t 1.2.3.4:80 -r 10.0.1.12 -w 2 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.3.5 一致性哈希(CONNHASH)
一致性哈希确保相同源 IP 或目标 IP 的请求始终路由到同一服务器。
┌─────────────────────────────────────────────────────────────────┐
│ 一致性哈希调度算法(CONNHASH) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 算法原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 哈希环: │ │
│ │ │ │
│ │ 0° │ │
│ │ │ │ │
│ │ │ ┌─────┐ │ │
│ │ │ │ RS1 │ ← 位置: hash(RS1) │ │
│ │ │ └─────┘ │ │
│ │ │ │ │
│ │ 270° ──┼─── 180° │ │
│ │ │ │ │
│ │ │ ┌─────┐ │ │
│ │ │ │ RS2 │ ← 位置: hash(RS2) │ │
│ │ │ └─────┘ │ │
│ │ │ │ │
│ │ │ ┌─────┐ │ │
│ │ │ │ RS3 │ ← 位置: hash(RS3) │ │
│ │ │ └─────┘ │ │
│ │ │ │ │
│ │ 90° │ │
│ │ │ │
│ │ 路由规则: │ │
│ │ hash(客户端IP) → 顺时针找到第一个服务器 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 会话保持原理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 客户端A: hash("203.0.113.50") → RS2 │ │
│ │ 客户端B: hash("198.51.100.100") → RS1 │ │
│ │ 客户端C: hash("192.0.2.1") → RS3 │ │
│ │ │ │
│ │ 后续请求: │ │
│ │ 客户端A的任何请求 → 始终路由到RS2 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • 需要会话保持的场景 │ │
│ │ • 缓存服务器(相同资源到同一缓存) │ │
│ │ • 有状态服务 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 配置: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # 基于源IP哈希 │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s sh │ │
│ │ │ │
│ │ # 基于目标IP哈希(适合缓存场景) │ │
│ │ ipvsadm -A -t 1.2.3.4:80 -s dh │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.3.6 连接模板
连接模板用于优化 TCP 连接复用,减少后端压力。
┌─────────────────────────────────────────────────────────────────┐
│ 连接模板(Connection Template) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 问题背景: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 传统模式: │ │
│ │ 每个HTTP请求创建一个新连接 │ │
│ │ │ │
│ │ Client ────→ L7 LB ────→ RS │ │
│ │ │ │ │
│ │ │ HTTP请求1 → 建立连接 → 处理 → 关闭连接 │ │
│ │ │ HTTP请求2 → 建立连接 → 处理 → 关闭连接 │ │
│ │ │ HTTP请求3 → 建立连接 → 处理 → 关闭连接 │ │
│ │ │ │
│ │ 问题:连接建立/关闭开销大 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 连接模板解决方案: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 同一客户端的请求复用同一后端连接: │ │
│ │ │ │
│ │ Client ────→ L7 LB ────→ RS │ │
│ │ │ │ │
│ │ │ HTTP请求1 → 建立连接 ───────────────────────┐ │ │
│ │ │ HTTP请求2 → 复用连接 ───────────────────────→│ │ │
│ │ │ HTTP请求3 → 复用连接 ───────────────────────→│ │ │
│ │ │ 连接保持打开 ─────────────┘ │ │
│ │ │ │
│ │ L7 LB维护: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 连接模板表: │ │ │
│ │ │ │ │ │
│ │ │ ClientIP:Port ↔ RS:Port ↔ 后端连接 │ │ │
│ │ │ │ │ │
│ │ │ 203.0.113.50:54321 ↔ RS1:8080 ↔ TCP连接1 │ │ │
│ │ │ 198.51.100.100:54322 ↔ RS2:8080 ↔ TCP连接2 │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Nginx连接复用配置: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ upstream backend { │ │
│ │ server 10.0.1.10:8080; │ │
│ │ │ │
│ │ # 启用连接复用 │ │
│ │ keepalive 100; │ │
│ │ keepalive_requests 1000; │ │
│ │ keepalive_timeout 60s; │ │
│ │ } │ │
│ │ │ │
│ │ location / { │ │
│ │ proxy_pass http://backend; │ │
│ │ │ │
│ │ # 启用HTTP长连接 │ │
│ │ proxy_http_version 1.1; │ │
│ │ proxy_set_header Connection ""; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 效果对比: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 指标 传统模式 连接复用模式 │ │
│ │ ────────────────────────────────────────────────────── │ │
│ │ 后端连接数 N个请求=N个 N个请求≈10-100个 │ │
│ │ TCP握手开销 N次 减少90%+ │ │
│ │ TIME_WAIT 大量 显著减少 │ │
│ │ 吞吐量 基准 提升2-3倍 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
连接池是一种通过复用已建立的TCP连接来优化网络通信性能的技术。在传统的HTTP短连接模式下,客户端每次发起请求都需要与服务器经历完整的TCP三次握手建立连接,传输数据后再通过四次挥手断开连接,这个过程会消耗大量的CPU资源、内存以及网络往返时间(RTT)。连接池的核心机制在于"借用与归还":当客户端第一次请求数据时,会建立一条TCP连接(即一个包含IP、端口和系统内核状态记录的"数据管道");数据传输完成后,该连接并不会立即关闭,而是被保留在池中以"空闲"状态待命;当后续有新的请求时,客户端直接从池中取出这条现成的连接复用,从而完全跳过了昂贵的三次握手过程。这种"一次握手,多次传输"的模式,极大地降低了服务器处理连接的开销和网络延迟,显著提升了高并发场景下的系统吞吐量。
1.4 LVS 与 DPVS 对比
┌─────────────────────────────────────────────────────────────────┐
│ LVS vs DPVS 对比 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ LVS(Linux Virtual Server): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 原理:内核模块 + Netfilter钩子 │ │
│ │ │ │
│ │ 数据包路径: │ │
│ │ 网卡 → 内核协议栈 → IPVS处理 → 转发 │ │
│ │ │ │
│ │ 优点: │ │
│ │ • 内核自带,无需额外安装 │ │
│ │ • 配置简单 │ │
│ │ • 稳定可靠 │ │
│ │ │ │
│ │ 缺点: │ │
│ │ • 性能受限于内核协议栈 │ │
│ │ • 中断和上下文切换开销 │ │
│ │ • 扩展性有限 │ │
│ │ │ │
│ │ 性能:~100万新建连接/秒,~10Gbps │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ DPVS(DPDK-based LVS): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 原理:用户态 + DPDK + 轮询模式 │ │
│ │ │ │
│ │ 数据包路径: │ │
│ │ 网卡DMA → 用户态内存 → 轻量协议栈 → 转发 │ │
│ │ │ │
│ │ 优点: │ │
│ │ • 绕过内核,极致性能 │ │
│ │ • 无中断开销 │ │
│ │ • 线性多核扩展 │ │
│ │ │ │
│ │ 缺点: │ │
│ │ • 部署复杂 │ │
│ │ • 需要DPDK支持 │ │
│ │ • 学习曲线陡峭 │ │
│ │ │ │
│ │ 性能:~1000万新建连接/秒,~100Gbps │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 选型建议: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 场景 推荐方案 │ │
│ │ ───────────────────────────────────────────────────────│ │
│ │ 中小规模(<100万并发) LVS │ │
│ │ 大规模(100万-1000万) DPVS │ │
│ │ 超大规模(>1000万) DPVS + 集群 │ │
│ │ 快速部署 LVS │ │
│ │ 极致性能 DPVS │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
LVS(Linux Virtual Server)与IPVS的关系可以理解为"整体架构"与"核心引擎":LVS是由章文嵩博士开发的开源负载均衡解决方案,而IPVS(IP Virtual Server)是LVS在内核中实现的具体模块,负责执行核心的流量调度。其工作原理是从上至下的协同配合:用户空间的管理工具 ipvsadm 负责接收管理员的指令(如添加虚拟服务和后端服务器),并将这些规则配置到内核空间的IPVS模块中;当客户端的数据包到达服务器时,会进入内核网络协议栈,IPVS模块会在数据包的流向路径上(如PREROUTING和INPUT链)进行拦截。根据预设的规则和调度算法(如轮询、最小连接数等),IPVS会修改数据包的目的地------例如在DR模式下修改目标MAC地址,或在NAT模式下修改目标IP地址------然后将数据包转发给选定的后端真实服务器,从而实现高效的分发。DPVS 的原理可以看作是 LVS 的"完全体"进化,它利用 DPDK 技术实现了"内核旁路",不再依赖 Linux 内核协议栈,而是直接在用户态接管网卡硬件,通过轮询机制和零拷贝技术,像工厂流水线一样高速处理数据包,从而彻底消除了内核中断和上下文切换的开销;它与 LVS 的核心区别在于:LVS 是在内核里"搭便车",利用内核自带的钩子修改数据包,虽然比传统软件快,但仍受限于内核锁和协议栈的繁琐处理,而 DPVS 则是"自己修路",在用户态自建了一套轻量级协议栈,将网卡与 CPU 核心一对一绑定,虽然部署和维护门槛更高,但能轻松跑满万兆网卡,专为应对互联网大厂那种每秒百万级新建连接的极端流量场景而生。