欢迎光临
我们一直在努力

LoadBalancer- 基础网络知识:负载均衡与 TCP / IP 协议栈的关联

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕LoadBalancer这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


文章目录

  • LoadBalancer – 基础网络知识:负载均衡与 TCP/IP 协议栈的关联 🌐
    • 什么是负载均衡?🤔
    • TCP/IP 协议栈回顾 📡
    • 负载均衡的分类:L4 vs L7 ⚖️
      • 四层负载均衡(L4 Load Balancing)
      • 七层负载均衡(L7 Load Balancing)
    • TCP 连接建立与负载均衡的关系 🔗
      • 模式一:透明代理(Direct Server Return, DSR)
      • 模式二:全代理(Full Proxy)
    • Java 中模拟简单的 L4 负载均衡器 💻
      • 如何测试?
    • 负载均衡与 IP 路由、NAT 的关系 🌍
      • SNAT(源地址转换)
      • DNAT(目标地址转换)
    • 健康检查:负载均衡的“眼睛” 👀
      • TCP 健康检查
      • HTTP 健康检查
    • 负载均衡算法详解 🧮
      • 1. 轮询(Round Robin)
      • 2. 加权轮询(Weighted Round Robin)
      • 3. 最少连接(Least Connections)
      • 4. 源 IP 哈希(IP Hash)
      • 5. 随机(Random)
    • TLS/SSL 终止与负载均衡 🔒
    • 负载均衡与 Keep-Alive、连接复用 🔄
    • 真实世界的负载均衡架构 🏗️
    • 负载均衡的局限性与挑战 ⚠️
      • 1. 状态同步问题
      • 2. 负载不均
      • 3. 单点故障
      • 4. 安全风险
    • 总结:负载均衡是 TCP/IP 之上的智能调度器 🧠

LoadBalancer – 基础网络知识:负载均衡与 TCP/IP 协议栈的关联 🌐

在现代分布式系统架构中,负载均衡(Load Balancer)扮演着至关重要的角色。它不仅能够提升系统的可用性和可扩展性,还能有效分摊流量压力、提高资源利用率。然而,要真正理解负载均衡的工作机制,必须深入其底层——TCP/IP 协议栈。本文将从基础网络知识出发,系统讲解负载均衡与 TCP/IP 协议栈之间的紧密联系,并通过 Java 代码示例、协议分析和可视化图表,帮助读者构建完整的认知体系。


什么是负载均衡?🤔

负载均衡是一种将网络请求或计算任务合理分配到多个服务器节点上的技术。它的核心目标是:

  • 提高系统吞吐量:避免单点过载,充分利用集群资源。
  • 增强高可用性:当某个后端节点宕机时,自动将流量导向健康节点。
  • 优化响应延迟:通过就近分发或智能调度策略减少用户等待时间。
  • 支持弹性伸缩:便于动态增减后端实例而不影响前端服务。

常见的负载均衡器类型包括:

  • 硬件负载均衡器(如 F5 BIG-IP)
  • 软件负载均衡器(如 Nginx、HAProxy、Envoy)
  • 云服务商提供的托管负载均衡服务(如 AWS ALB/NLB、阿里云 SLB)

无论哪种形式,它们都运行在 TCP/IP 网络之上,因此理解 TCP/IP 协议栈是掌握负载均衡原理的前提。


TCP/IP 协议栈回顾 📡

TCP/IP 是互联网通信的基础协议栈,采用四层模型(有时也按 OSI 七层简化为五层):

  • 链路层(Link Layer):负责物理网络上的数据帧传输(如以太网、Wi-Fi)。
  • 网络层(Internet Layer):处理 IP 地址路由,核心协议是 IP(Internet Protocol)。
  • 传输层(Transport Layer):提供端到端通信,主要协议有 TCP(Transmission Control Protocol) 和 UDP(User Datagram Protocol)。
  • 应用层(Application Layer):承载具体业务逻辑,如 HTTP、HTTPS、FTP、DNS 等。
  • #mermaid-svg-QMwHipqZR2ICdDNi{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QMwHipqZR2ICdDNi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QMwHipqZR2ICdDNi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QMwHipqZR2ICdDNi .error-icon{fill:#552222;}#mermaid-svg-QMwHipqZR2ICdDNi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QMwHipqZR2ICdDNi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QMwHipqZR2ICdDNi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QMwHipqZR2ICdDNi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QMwHipqZR2ICdDNi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QMwHipqZR2ICdDNi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QMwHipqZR2ICdDNi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QMwHipqZR2ICdDNi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QMwHipqZR2ICdDNi .marker.cross{stroke:#333333;}#mermaid-svg-QMwHipqZR2ICdDNi svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QMwHipqZR2ICdDNi p{margin:0;}#mermaid-svg-QMwHipqZR2ICdDNi .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-QMwHipqZR2ICdDNi .cluster-label text{fill:#333;}#mermaid-svg-QMwHipqZR2ICdDNi .cluster-label span{color:#333;}#mermaid-svg-QMwHipqZR2ICdDNi .cluster-label span p{background-color:transparent;}#mermaid-svg-QMwHipqZR2ICdDNi .label text,#mermaid-svg-QMwHipqZR2ICdDNi span{fill:#333;color:#333;}#mermaid-svg-QMwHipqZR2ICdDNi .node rect,#mermaid-svg-QMwHipqZR2ICdDNi .node circle,#mermaid-svg-QMwHipqZR2ICdDNi .node ellipse,#mermaid-svg-QMwHipqZR2ICdDNi .node polygon,#mermaid-svg-QMwHipqZR2ICdDNi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QMwHipqZR2ICdDNi .rough-node .label text,#mermaid-svg-QMwHipqZR2ICdDNi .node .label text,#mermaid-svg-QMwHipqZR2ICdDNi .image-shape .label,#mermaid-svg-QMwHipqZR2ICdDNi .icon-shape .label{text-anchor:middle;}#mermaid-svg-QMwHipqZR2ICdDNi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QMwHipqZR2ICdDNi .rough-node .label,#mermaid-svg-QMwHipqZR2ICdDNi .node .label,#mermaid-svg-QMwHipqZR2ICdDNi .image-shape .label,#mermaid-svg-QMwHipqZR2ICdDNi .icon-shape .label{text-align:center;}#mermaid-svg-QMwHipqZR2ICdDNi .node.clickable{cursor:pointer;}#mermaid-svg-QMwHipqZR2ICdDNi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QMwHipqZR2ICdDNi .arrowheadPath{fill:#333333;}#mermaid-svg-QMwHipqZR2ICdDNi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QMwHipqZR2ICdDNi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QMwHipqZR2ICdDNi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QMwHipqZR2ICdDNi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QMwHipqZR2ICdDNi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QMwHipqZR2ICdDNi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QMwHipqZR2ICdDNi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QMwHipqZR2ICdDNi .cluster text{fill:#333;}#mermaid-svg-QMwHipqZR2ICdDNi .cluster span{color:#333;}#mermaid-svg-QMwHipqZR2ICdDNi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QMwHipqZR2ICdDNi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QMwHipqZR2ICdDNi rect.text{fill:none;stroke-width:0;}#mermaid-svg-QMwHipqZR2ICdDNi .icon-shape,#mermaid-svg-QMwHipqZR2ICdDNi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QMwHipqZR2ICdDNi .icon-shape p,#mermaid-svg-QMwHipqZR2ICdDNi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QMwHipqZR2ICdDNi .icon-shape .label rect,#mermaid-svg-QMwHipqZR2ICdDNi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QMwHipqZR2ICdDNi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QMwHipqZR2ICdDNi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QMwHipqZR2ICdDNi :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    应用层 HTTP/HTTPS

    传输层 TCP/UDP

    网络层 IP

    链路层 Ethernet/WiFi

    负载均衡器通常工作在传输层(L4) 或 应用层(L7),这直接决定了其对 TCP/IP 协议栈的介入深度。


    负载均衡的分类:L4 vs L7 ⚖️

    四层负载均衡(L4 Load Balancing)

    四层负载均衡基于 IP + 端口 进行流量分发。它工作在传输层,只解析到 TCP/UDP 头部,不关心应用层内容。

    • 优点:性能高、延迟低、资源消耗少。
    • 缺点:无法基于 URL、Header、Cookie 等应用层信息做决策。
    • 典型场景:数据库代理、游戏服务器、非 HTTP 协议服务。

    例如,一个 L4 负载均衡器接收到目标为 192.168.1.100:80 的 TCP 包,会根据算法(如轮询、最少连接)选择后端服务器(如 10.0.0.1:8080),然后通过 NAT(网络地址转换)转发数据包。

    七层负载均衡(L7 Load Balancing)

    七层负载均衡深入到应用层,能解析 HTTP 请求的内容(如路径 /api/user、Host 头、User-Agent 等)。

    • 优点:调度更智能,支持内容路由、SSL 终止、缓存、压缩等高级功能。
    • 缺点:性能开销大,需完整解析应用层协议。
    • 典型场景:Web 应用、微服务 API 网关。

    例如,Nginx 可以根据请求的 Host: api.example.com 将流量转发到不同的后端服务组。

    #mermaid-svg-WC8NAdq4PXaBSHX2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WC8NAdq4PXaBSHX2 .error-icon{fill:#552222;}#mermaid-svg-WC8NAdq4PXaBSHX2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WC8NAdq4PXaBSHX2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .marker.cross{stroke:#333333;}#mermaid-svg-WC8NAdq4PXaBSHX2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WC8NAdq4PXaBSHX2 p{margin:0;}#mermaid-svg-WC8NAdq4PXaBSHX2 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .cluster-label text{fill:#333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .cluster-label span{color:#333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .cluster-label span p{background-color:transparent;}#mermaid-svg-WC8NAdq4PXaBSHX2 .label text,#mermaid-svg-WC8NAdq4PXaBSHX2 span{fill:#333;color:#333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .node rect,#mermaid-svg-WC8NAdq4PXaBSHX2 .node circle,#mermaid-svg-WC8NAdq4PXaBSHX2 .node ellipse,#mermaid-svg-WC8NAdq4PXaBSHX2 .node polygon,#mermaid-svg-WC8NAdq4PXaBSHX2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WC8NAdq4PXaBSHX2 .rough-node .label text,#mermaid-svg-WC8NAdq4PXaBSHX2 .node .label text,#mermaid-svg-WC8NAdq4PXaBSHX2 .image-shape .label,#mermaid-svg-WC8NAdq4PXaBSHX2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-WC8NAdq4PXaBSHX2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WC8NAdq4PXaBSHX2 .rough-node .label,#mermaid-svg-WC8NAdq4PXaBSHX2 .node .label,#mermaid-svg-WC8NAdq4PXaBSHX2 .image-shape .label,#mermaid-svg-WC8NAdq4PXaBSHX2 .icon-shape .label{text-align:center;}#mermaid-svg-WC8NAdq4PXaBSHX2 .node.clickable{cursor:pointer;}#mermaid-svg-WC8NAdq4PXaBSHX2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .arrowheadPath{fill:#333333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WC8NAdq4PXaBSHX2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WC8NAdq4PXaBSHX2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WC8NAdq4PXaBSHX2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WC8NAdq4PXaBSHX2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WC8NAdq4PXaBSHX2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WC8NAdq4PXaBSHX2 .cluster text{fill:#333;}#mermaid-svg-WC8NAdq4PXaBSHX2 .cluster span{color:#333;}#mermaid-svg-WC8NAdq4PXaBSHX2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WC8NAdq4PXaBSHX2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WC8NAdq4PXaBSHX2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-WC8NAdq4PXaBSHX2 .icon-shape,#mermaid-svg-WC8NAdq4PXaBSHX2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WC8NAdq4PXaBSHX2 .icon-shape p,#mermaid-svg-WC8NAdq4PXaBSHX2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WC8NAdq4PXaBSHX2 .icon-shape .label rect,#mermaid-svg-WC8NAdq4PXaBSHX2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WC8NAdq4PXaBSHX2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WC8NAdq4PXaBSHX2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WC8NAdq4PXaBSHX2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    HTTP Request

    Host: web.example.com

    Host: api.example.com

    Path: /static

    Client

    L7 Load Balancer

    WebServer1

    APIServer1

    StaticServer1

    💡 注意:L7 负载均衡本质上仍依赖于底层的 TCP 连接。每个客户端请求可能触发两个独立的 TCP 连接:

  • 客户端 ↔ 负载均衡器
  • 负载均衡器 ↔ 后端服务器
  • 这种“连接拆分”机制是理解 L7 负载均衡的关键。


    TCP 连接建立与负载均衡的关系 🔗

    要理解负载均衡如何与 TCP/IP 交互,必须回顾 TCP 三次握手 过程:

  • SYN:客户端 → 服务器(发起连接)
  • SYN-ACK:服务器 → 客户端(确认并回应)
  • ACK:客户端 → 服务器(完成握手)
  • 在 L4 负载均衡中,负载均衡器通常作为 透明代理 或 全代理 参与此过程。

    模式一:透明代理(Direct Server Return, DSR)

    • 负载均衡器仅修改目标 MAC 地址(链路层),不改变 IP 和端口。
    • 后端服务器直接响应客户端,绕过负载均衡器。
    • 优点:减少 LB 出向带宽压力。
    • 缺点:配置复杂,需同一子网,不支持 NAT。

    模式二:全代理(Full Proxy)

    • 负载均衡器终止客户端的 TCP 连接,并新建一个到后端的 TCP 连接。
    • 客户端和后端之间存在 两个独立的 TCP 连接。
    • 这是最常见的模式,尤其适用于 L7 负载均衡。

    Backend

    Load Balancer

    Client

    Backend

    Load Balancer

    Client

    #mermaid-svg-4LfOQEU1tksimHCd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4LfOQEU1tksimHCd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4LfOQEU1tksimHCd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4LfOQEU1tksimHCd .error-icon{fill:#552222;}#mermaid-svg-4LfOQEU1tksimHCd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4LfOQEU1tksimHCd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4LfOQEU1tksimHCd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4LfOQEU1tksimHCd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4LfOQEU1tksimHCd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4LfOQEU1tksimHCd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4LfOQEU1tksimHCd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4LfOQEU1tksimHCd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4LfOQEU1tksimHCd .marker.cross{stroke:#333333;}#mermaid-svg-4LfOQEU1tksimHCd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4LfOQEU1tksimHCd p{margin:0;}#mermaid-svg-4LfOQEU1tksimHCd .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4LfOQEU1tksimHCd text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4LfOQEU1tksimHCd .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4LfOQEU1tksimHCd .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-4LfOQEU1tksimHCd .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4LfOQEU1tksimHCd .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4LfOQEU1tksimHCd #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4LfOQEU1tksimHCd .sequenceNumber{fill:white;}#mermaid-svg-4LfOQEU1tksimHCd #sequencenumber{fill:#333;}#mermaid-svg-4LfOQEU1tksimHCd #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4LfOQEU1tksimHCd .messageText{fill:#333;stroke:none;}#mermaid-svg-4LfOQEU1tksimHCd .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4LfOQEU1tksimHCd .labelText,#mermaid-svg-4LfOQEU1tksimHCd .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4LfOQEU1tksimHCd .loopText,#mermaid-svg-4LfOQEU1tksimHCd .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4LfOQEU1tksimHCd .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4LfOQEU1tksimHCd .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4LfOQEU1tksimHCd .noteText,#mermaid-svg-4LfOQEU1tksimHCd .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4LfOQEU1tksimHCd .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4LfOQEU1tksimHCd .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4LfOQEU1tksimHCd .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4LfOQEU1tksimHCd .actorPopupMenu{position:absolute;}#mermaid-svg-4LfOQEU1tksimHCd .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-4LfOQEU1tksimHCd .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4LfOQEU1tksimHCd .actor-man circle,#mermaid-svg-4LfOQEU1tksimHCd line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4LfOQEU1tksimHCd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Application data flows through LB

    SYN (to VIP:80)

    SYN-ACK

    ACK (TCP handshake complete)

    SYN (to 10.0.0.1:8080)

    SYN-ACK

    ACK (TCP handshake complete)

    在这种模式下,负载均衡器完全掌控两个 TCP 连接的状态,可以实现连接池复用、超时控制、SSL 解密等高级功能。


    Java 中模拟简单的 L4 负载均衡器 💻

    虽然生产环境通常使用 Nginx 或专用 LB,但我们可以通过 Java 编写一个简易的 TCP 层负载均衡器,加深对协议栈的理解。

    以下是一个基于 java.net 包的简单实现,它监听本地 8080 端口,并将连接轮询转发到两个后端服务器。

    import java.io.*;
    import java.net.*;
    import java.util.*;
    import java.util.concurrent.*;

    public class SimpleL4LoadBalancer {
    private static final List<String> BACKENDS = Arrays.asList(
    "127.0.0.1:9001",
    "127.0.0.1:9002"
    );
    private static int currentBackendIndex = 0;
    private static final ExecutorService executor = Executors.newCachedThreadPool();

    public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = new ServerSocket(8080);
    System.out.println("🚀 L4 Load Balancer listening on :8080");

    while (true) {
    Socket clientSocket = serverSocket.accept();
    System.out.println("📥 New client connected: " + clientSocket.getInetAddress());

    // 轮询选择后端
    String backend = getNextBackend();
    String[] parts = backend.split(":");
    String host = parts[0];
    int port = Integer.parseInt(parts[1]);

    executor.submit(() -> handleConnection(clientSocket, host, port));
    }
    }

    private static String getNextBackend() {
    synchronized (BACKENDS) {
    String backend = BACKENDS.get(currentBackendIndex);
    currentBackendIndex = (currentBackendIndex + 1) % BACKENDS.size();
    return backend;
    }
    }

    private static void handleConnection(Socket clientSocket, String backendHost, int backendPort) {
    try (Socket backendSocket = new Socket(backendHost, backendPort)) {
    // 创建双向数据流管道
    Pipe pipe1 = new Pipe(clientSocket.getInputStream(), backendSocket.getOutputStream());
    Pipe pipe2 = new Pipe(backendSocket.getInputStream(), clientSocket.getOutputStream());

    Thread t1 = new Thread(pipe1);
    Thread t2 = new Thread(pipe2);

    t1.start();
    t2.start();

    t1.join();
    t2.join();

    } catch (Exception e) {
    System.err.println("❌ Error handling connection: " + e.getMessage());
    } finally {
    try {
    clientSocket.close();
    } catch (IOException ignored) {}
    }
    }

    static class Pipe implements Runnable {
    private final InputStream in;
    private final OutputStream out;

    Pipe(InputStream in, OutputStream out) {
    this.in = in;
    this.out = out;
    }

    @Override
    public void run() {
    byte[] buffer = new byte[4096];
    try {
    int bytesRead;
    while ((bytesRead = in.read(buffer)) != 1) {
    out.write(buffer, 0, bytesRead);
    out.flush();
    }
    } catch (IOException e) {
    // 连接关闭或异常,正常退出
    }
    }
    }
    }

    如何测试?

  • 启动两个简单的 TCP 服务(可用 Python 快速搭建):

    # 终端1
    python3 -c "import socket; s=socket.socket(); s.bind(('127.0.0.1',9001)); s.listen(); c,a=s.accept(); print('Backend1 got:', c.recv(1024).decode())"

    # 终端2
    python3 -c "import socket; s=socket.socket(); s.bind(('127.0.0.1',9002)); s.listen(); c,a=s.accept(); print('Backend2 got:', c.recv(1024).decode())"

  • 运行 Java 负载均衡器。

  • 使用 telnet 或 nc 发送数据:

    echo "Hello from client" | nc localhost 8080

  • 你会看到请求被轮询分发到两个后端。

    🔍 注意:此实现是 全代理模式,每个客户端连接都会在 LB 和后端之间新建一个 TCP 连接。这正是 L4 负载均衡的典型行为。


    负载均衡与 IP 路由、NAT 的关系 🌍

    负载均衡器常部署在网络边界,涉及复杂的 IP 转换机制,尤其是 NAT(Network Address Translation)。

    SNAT(源地址转换)

    当后端服务器需要回包给客户端时,若其默认网关不是负载均衡器,则可能直接回包,导致客户端收不到响应(因为源 IP 不是 LB 的 VIP)。此时需在 LB 上做 SNAT,将客户端 IP 替换为 LB 自身 IP,使后端回包经过 LB。

    DNAT(目标地址转换)

    这是负载均衡的核心:将发往虚拟 IP(VIP)的流量,目标地址改为真实后端 IP。

    例如:

    • 客户端发送:src=203.0.113.5, dst=198.51.100.10:80
    • LB 执行 DNAT:src=203.0.113.5, dst=10.0.0.5:8080
    • 后端收到包后,认为来自 203.0.113.5,直接回包:src=10.0.0.5, dst=203.0.113.5
    • 若未做 SNAT,客户端收到源 IP 为 10.0.0.5 的包,会丢弃(因期待 198.51.100.10 的响应)

    因此,全代理模式天然避免了这个问题,因为 LB 终止了连接,后端只需回包给 LB。


    健康检查:负载均衡的“眼睛” 👀

    负载均衡器必须知道哪些后端是健康的,否则会将流量导向宕机节点。健康检查机制通常基于 TCP 或 HTTP。

    TCP 健康检查

    • LB 尝试与后端建立 TCP 连接。
    • 若成功(收到 SYN-ACK),则认为健康。
    • 适用于任意 TCP 服务。

    HTTP 健康检查

    • LB 发送 HTTP GET 请求(如 /health)。
    • 若返回 2xx 状态码,则认为健康。
    • 更精确,但仅适用于 HTTP 服务。

    Java 示例:实现一个简单的健康检查线程

    import java.io.IOException;
    import java.net.Socket;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;

    public class HealthChecker {
    private final String host;
    private final int port;
    private volatile boolean isHealthy = true;

    public HealthChecker(String host, int port) {
    this.host = host;
    this.port = port;
    startChecking();
    }

    private void startChecking() {
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    scheduler.scheduleAtFixedRate(this::check, 0, 5, TimeUnit.SECONDS);
    }

    private void check() {
    try (Socket socket = new Socket()) {
    socket.connect(new InetSocketAddress(host, port), 1000); // 1秒超时
    isHealthy = true;
    System.out.println("✅ " + host + ":" + port + " is healthy");
    } catch (IOException e) {
    isHealthy = false;
    System.err.println("❌ " + host + ":" + port + " is DOWN");
    }
    }

    public boolean isHealthy() {
    return isHealthy;
    }
    }

    在真实负载均衡器中,健康检查结果会动态更新后端列表,确保流量只流向健康节点。


    负载均衡算法详解 🧮

    负载均衡的核心是调度算法。常见算法包括:

    1. 轮询(Round Robin)

    依次将请求分发给每个后端。简单公平,但忽略服务器性能差异。

    2. 加权轮询(Weighted Round Robin)

    为每个后端分配权重,高性能服务器获得更高权重,接收更多请求。

    3. 最少连接(Least Connections)

    将新请求分配给当前活跃连接数最少的后端。适合长连接场景。

    4. 源 IP 哈希(IP Hash)

    根据客户端 IP 计算哈希值,映射到固定后端。实现会话保持(Session Persistence)。

    5. 随机(Random)

    随机选择后端,配合健康检查可实现简单负载分散。

    在 Java 中实现加权轮询:

    import java.util.*;

    public class WeightedRoundRobin {
    private static class Server {
    String address;
    int weight;
    int currentWeight;

    Server(String address, int weight) {
    this.address = address;
    this.weight = weight;
    this.currentWeight = 0;
    }
    }

    private final List<Server> servers = new ArrayList<>();
    private int totalWeight = 0;

    public void addServer(String address, int weight) {
    servers.add(new Server(address, weight));
    totalWeight += weight;
    }

    public String getNextServer() {
    if (servers.isEmpty()) return null;

    Server selected = null;
    int maxWeight = Integer.MIN_VALUE;

    for (Server server : servers) {
    server.currentWeight += server.weight;
    if (selected == null || server.currentWeight > maxWeight) {
    selected = server;
    maxWeight = server.currentWeight;
    }
    }

    if (selected != null) {
    selected.currentWeight -= totalWeight;
    return selected.address;
    }

    return servers.get(0).address; // fallback
    }

    public static void main(String[] args) {
    WeightedRoundRobin wrr = new WeightedRoundRobin();
    wrr.addServer("10.0.0.1:8080", 3);
    wrr.addServer("10.0.0.2:8080", 1);

    // 模拟10次请求
    for (int i = 0; i < 10; i++) {
    System.out.println("➡️ Request " + (i+1) + " -> " + wrr.getNextServer());
    }
    }
    }

    输出示例:

    ➡️ Request 1 -> 10.0.0.1:8080
    ➡️ Request 2 -> 10.0.0.1:8080
    ➡️ Request 3 -> 10.0.0.1:8080
    ➡️ Request 4 -> 10.0.0.2:8080
    ➡️ Request 5 -> 10.0.0.1:8080


    TLS/SSL 终止与负载均衡 🔒

    现代 Web 应用普遍使用 HTTPS(HTTP over TLS)。负载均衡器常承担 SSL 终止(SSL Termination) 角色:

    • 客户端 ↔ LB:HTTPS(加密)
    • LB ↔ 后端:HTTP(明文,或内网 TLS)

    这样做的好处:

    • 减轻后端服务器的 CPU 负担(TLS 加解密很耗资源)
    • 统一管理证书
    • 便于 L7 路由(需解密后才能读取 HTTP 内容)

    但需注意安全边界:内网通信应受信任,或启用 mTLS(双向 TLS)。

    在 Java 中,若需实现 SSL 终止,可使用 javax.net.ssl 包创建 SSLServerSocket,但这超出本文范围。生产环境推荐使用 Nginx 或云 LB。


    负载均衡与 Keep-Alive、连接复用 🔄

    HTTP/1.1 默认启用 Connection: keep-alive,允许在一个 TCP 连接上发送多个请求。这对负载均衡有重要影响:

    • L7 负载均衡器:通常为每个客户端连接维护一个后端连接池。一个客户端连接可能复用多个后端连接,也可能一个后端连接服务多个客户端(取决于实现)。
    • 连接复用 可显著减少 TCP 握手开销,提升性能。

    但需注意:会话粘性(Sticky Session) 在连接复用下可能失效,因为后续请求可能被复用到不同后端。


    真实世界的负载均衡架构 🏗️

    大型系统通常采用多层负载均衡:

    #mermaid-svg-01It16RwxJcwSkVJ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-01It16RwxJcwSkVJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-01It16RwxJcwSkVJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-01It16RwxJcwSkVJ .error-icon{fill:#552222;}#mermaid-svg-01It16RwxJcwSkVJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-01It16RwxJcwSkVJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-01It16RwxJcwSkVJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-01It16RwxJcwSkVJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-01It16RwxJcwSkVJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-01It16RwxJcwSkVJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-01It16RwxJcwSkVJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-01It16RwxJcwSkVJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-01It16RwxJcwSkVJ .marker.cross{stroke:#333333;}#mermaid-svg-01It16RwxJcwSkVJ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-01It16RwxJcwSkVJ p{margin:0;}#mermaid-svg-01It16RwxJcwSkVJ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-01It16RwxJcwSkVJ .cluster-label text{fill:#333;}#mermaid-svg-01It16RwxJcwSkVJ .cluster-label span{color:#333;}#mermaid-svg-01It16RwxJcwSkVJ .cluster-label span p{background-color:transparent;}#mermaid-svg-01It16RwxJcwSkVJ .label text,#mermaid-svg-01It16RwxJcwSkVJ span{fill:#333;color:#333;}#mermaid-svg-01It16RwxJcwSkVJ .node rect,#mermaid-svg-01It16RwxJcwSkVJ .node circle,#mermaid-svg-01It16RwxJcwSkVJ .node ellipse,#mermaid-svg-01It16RwxJcwSkVJ .node polygon,#mermaid-svg-01It16RwxJcwSkVJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-01It16RwxJcwSkVJ .rough-node .label text,#mermaid-svg-01It16RwxJcwSkVJ .node .label text,#mermaid-svg-01It16RwxJcwSkVJ .image-shape .label,#mermaid-svg-01It16RwxJcwSkVJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-01It16RwxJcwSkVJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-01It16RwxJcwSkVJ .rough-node .label,#mermaid-svg-01It16RwxJcwSkVJ .node .label,#mermaid-svg-01It16RwxJcwSkVJ .image-shape .label,#mermaid-svg-01It16RwxJcwSkVJ .icon-shape .label{text-align:center;}#mermaid-svg-01It16RwxJcwSkVJ .node.clickable{cursor:pointer;}#mermaid-svg-01It16RwxJcwSkVJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-01It16RwxJcwSkVJ .arrowheadPath{fill:#333333;}#mermaid-svg-01It16RwxJcwSkVJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-01It16RwxJcwSkVJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-01It16RwxJcwSkVJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-01It16RwxJcwSkVJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-01It16RwxJcwSkVJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-01It16RwxJcwSkVJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-01It16RwxJcwSkVJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-01It16RwxJcwSkVJ .cluster text{fill:#333;}#mermaid-svg-01It16RwxJcwSkVJ .cluster span{color:#333;}#mermaid-svg-01It16RwxJcwSkVJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-01It16RwxJcwSkVJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-01It16RwxJcwSkVJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-01It16RwxJcwSkVJ .icon-shape,#mermaid-svg-01It16RwxJcwSkVJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-01It16RwxJcwSkVJ .icon-shape p,#mermaid-svg-01It16RwxJcwSkVJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-01It16RwxJcwSkVJ .icon-shape .label rect,#mermaid-svg-01It16RwxJcwSkVJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-01It16RwxJcwSkVJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-01It16RwxJcwSkVJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-01It16RwxJcwSkVJ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Anycast

    Geo-routing

    Geo-routing

    Internet

    DNS

    Global Load Balancer (e.g., AWS Route 53)

    Regional LB (e.g., AWS ALB)

    Regional LB

    Availability Zone 1

    Availability Zone 2

    AppServer1

    AppServer2

    AppServer3

    AppServer4

    这种架构结合了:

    • DNS 负载均衡(基于地理位置)
    • L7 负载均衡(应用层路由)
    • 跨可用区容灾

    AWS 的 Application Load Balancer 就是一个典型的 L7 负载均衡服务,支持基于路径、主机名的路由。

    而 Cloudflare 则在全球边缘节点提供智能负载均衡,结合 Anycast 技术实现低延迟。


    负载均衡的局限性与挑战 ⚠️

    尽管负载均衡强大,但也面临挑战:

    1. 状态同步问题

    若应用是有状态的(如 WebSocket、游戏会话),需确保同一用户始终路由到同一后端(会话保持)。可通过 IP Hash 或 Cookie 实现。

    2. 负载不均

    即使使用“最少连接”,若请求处理时间差异大,仍可能导致负载倾斜。需结合实时指标(CPU、内存)动态调整。

    3. 单点故障

    负载均衡器自身可能成为瓶颈。解决方案:部署 LB 集群 + VIP 漂移(如 Keepalived)。

    4. 安全风险

    LB 暴露在公网,易受 DDoS 攻击。需配合 WAF(Web 应用防火墙)和速率限制。


    总结:负载均衡是 TCP/IP 之上的智能调度器 🧠

    负载均衡并非孤立组件,而是深度嵌入 TCP/IP 协议栈的智能流量调度器。从 L4 的 IP/端口转发,到 L7 的 HTTP 内容解析,每一步都依赖对底层网络协议的精准操控。

    • L4 负载均衡:高效、透明,适用于通用 TCP/UDP 服务。
    • L7 负载均衡:灵活、智能,适用于 Web 和微服务架构。
    • Java 实现:虽不适合生产,但有助于理解连接代理、线程模型和协议交互。
    • 真实架构:多层 LB + 健康检查 + SSL 终止 + 全球分发,构成现代高可用系统基石。

    理解负载均衡与 TCP/IP 的关联,不仅能帮助我们设计更健壮的系统,还能在排查网络问题时快速定位瓶颈——是 LB 配置错误?还是 TCP 连接耗尽?或是后端响应慢?

    最后,推荐阅读以下资源深入学习:

    • RFC 793 – Transmission Control Protocol
    • NGINX Load Balancing Guide
    • Cloudflare Learning: What is a Load Balancer?

    愿你在分布式系统的海洋中,驾驭好负载均衡这艘巨轮,驶向高可用、高性能的彼岸!⛵


    🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

    赞(0)
    未经允许不得转载:171主机测评 » LoadBalancer- 基础网络知识:负载均衡与 TCP / IP 协议栈的关联
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址