
说实话我第一次看到“HAProxy实验”这个标题时就想起当年自己搭第一个负载均衡集群时手忙脚乱的样子。那时候连四层和七层都分不清配置写错了就在那一个劲地重启服务日志又没开排查了半天才发现是后端健康检查路径不对。这个实验内容其实非常有价值——它不只是教你装一个HAProxy而是把负载均衡从理论拉到了落地让你真正理解流量分发、故障转移、会话保持这些生产环境里的核心概念。这篇文章适合刚接触负载均衡的后端研发、运维新手以及那些想在公司内部做一个高可用演示项目的同学。我会按实验环境准备、安装方式对比、核心配置拆解、完整实操、常见坑这五条线来讲每一个环节都带上我这么多年攒下来的经验和踩坑记录。1. 实验环境与整体思路1.1 为什么要拿 HAProxy 做这个实验负载均衡这个领域里可选的东西不少Nginx能反代也能做负载均衡LVS性能强悍但配置复杂得让人头大云厂商的LB又是个黑盒。而HAProxy选择了一条非常务实的路线专注做四层和七层的负载均衡配置简单直接文档清晰性能在软件负载均衡里属于顶尖水平。很多人知道HAProxy能扛几十万并发但实验课的意义不在于跑出一个夸张的数字而在于让你把负载均衡的三个核心能力彻底练明白请求分发、健康检查、故障转移。我做这个实验时给自己定了一个目标不做那种“照着文档敲一遍就完事”的练习而是把HAProxy当成生产环境里的一个真实组件来对待。这意味着要考虑它如何优雅地启动、如何热加载配置、如何在后端挂掉的时候自动摘除节点、如何通过监控页面观察运行状态。这些内容才是工作里真正用得上的技能比单纯记几个配置参数重要得多。1.2 实验拓扑准备好了吗先规划一下整个实验的环境。我的设计是一台HAProxy节点、两台后端Web服务器三台机器处于同一个内网网段。这种拓扑最简单也最容易排查问题。如果你手头机器不够用Docker在单机上起三个容器也行但要注意端口映射别搞混了。拓扑规划如下客户端就是你自己的电脑或者一台测试机访问 HAProxy 的 80 端口HAProxy 节点192.168.1.10操作系统 Ubuntu 20.04CentOS 也行命令差异我会指出来后端节点 Web1192.168.1.11跑 Nginx 或随便什么 HTTP 服务监听 8080 端口后端节点 Web2192.168.1.12跑同样的服务监听 8080 端口我特意让后端服务监听 8080 而不是默认的 80其实是有用意的。这样能模拟真实场景中业务服务端口与代理端口分离的架构也避免和 HAProxy 自己有端口冲突。后端服务我建议用最简单的方式先跑起来把变量控制到最少等整个链路通了再去换更复杂的应用。2. 安装部署两种方式选一种就够2.1 通过系统包管理器安装最快路径如果你就是想快速看到实验效果系统自带的包管理器是最省事的选择。Ubuntu/Debian 系列执行这几行apt update apt install -y haproxyCentOS/RHEL 系列对应的是yum install -y haproxy安装完成后先别急着配置用haproxy -v看一下版本。为什么要先确认版本因为不同版本的配置语法差异不小比如 1.5 时代没有nbthread1.8 才开始支持多线程。你从网上搜到的很多教程如果没标注版本抄过来可能直接报错。Ubuntu 20.04 自带的是 2.0 版本CentOS 7 自带的是 1.5差别非常大。这一步务必做虽然简单但能帮你省掉后面至少半小时的排查时间。包管理器安装的最大优势是后续可以用systemctl start haproxy来启动开机自启也能直接设好。我本地测试环境基本都是这种装法完整看一遍功能没问题。2.2 源码编译安装版本和功能都要自己掌控如果你想要最新版本或者需要自定义编译特性比如换一个高性能的 SSL 库、加上对 Lua 脚本的支持那就要走源码编译这条路。别一听编译就觉得难HAProxy 的编译流程在同类项目里已经算相当顺畅的了。先在哈官方网站下载对应版本的源码包并解压然后安装编译要用的依赖。Ubuntu 下这样准备apt install -y build-essential libpcre3-dev libssl-dev zlib1g-dev libsystemd-dev接着执行编译make clean make -j4 TARGETlinux-glibc USE_OPENSSL1 USE_PCRE1 USE_ZLIB1 USE_SYSTEMD1 make install这里我解释几个关键参数的含义。TARGETlinux-glibc是告诉编译器目标平台是 Linux 且采用 glibc它直接决定底层用到哪些系统调用和优化方式老教程里常见的linux2628是针对 Linux 2.6.28 内核的现在新系统直接用linux-glibc最稳妥。USE_OPENSSL1打开 HTTPS 支持做七层负载均衡时如果你想终结 TLS 证书这个开关必须开。USE_PCRE1启用正则表达式支持ACL 规则里用到正则时性能会好很多。编译完成之后make install会把二进制装到/usr/local/sbin/haproxy。这种位置和包管理器安装的/usr/sbin/haproxy不一样所以配置文件路径、日志输出方式都需要你自己动手配置 systemd 单位文件。我个人的建议是第一次做实验用包管理器安装等你想深入钻研功能和性能时再切换到源码编译也不迟。3. 核心配置讲解看懂 haproxy.cfg 并不难3.1 配置文件的三段式结构HAProxy 的配置看起来参数繁多其实骨架非常清晰就三段global、defaults、以及后面跟随的具体实例frontend、backend、listen。我常用一个比喻来解释global 是给程序本体设置运行参数defaults 是给所有实例提供默认值frontend 和 backend 则是真正定义“怎么接客”和“找谁办事”的逻辑。一个最简的完整配置长这样global log /dev/log local0 info maxconn 50000 user haproxy group haproxy defaults log global mode http option httplog option dontlognull retries 3 timeout connect 5000 timeout client 50000 timeout server 50000 frontend web_front bind *:80 default_backend web_servers backend web_servers balance roundrobin option httpchk GET /health server web1 192.168.1.11:8080 check inter 2000 fall 3 rise 2 server web2 192.168.1.12:8080 check inter 2000 fall 3 rise 2这个配置里的 timeout 三项我多说一句timeout connect是 HAProxy 向后端发起 TCP 连接的超时时间timeout client是等待客户端发送请求的超时时间timeout server是等待后端响应的超时时间。很多新手只设置 connect后面遇到接口偶发超时的情况就开始怀疑后端代码有问题其实很可能是 server 超时设得太短。这三个值我建议在实验阶段就按上面这个比例来连接超时短一点快速失败读写超时长一点给业务留够处理时间。3.2 负载均衡算法怎么选从实验到生产负载均衡算法是 HAProxy 分发请求的策略核心挑错了直接影响用户体验。我在这儿把最常见的四种算法做成了表格方便对照。算法分发的依据适合的场景注意点roundrobin轮流分发后端处理能力均匀、短连接请求动态权重调整会平滑生效leastconn当前连接数最少者优先长连接、处理耗时差异大的请求需要等待连接建立后才能评估source客户端源IP的哈希需要按IP固定的会话保持IP变化会导致会话不连续uri请求URI的哈希缓存类服务、API网关同一URI会被分到同一后端我在做实验的时候建议先把roundrobin跑通它是一种最容易观察效果的算法。然后在第二个阶段换成leastconn你会发现在请求处理时间不均匀的情况下leastconn 的分配结果更科学因为 HAProxy 能感知到每个后端当前活跃的连接数从而找“最闲”的那个节点。至于 source 和 uri后面讲到会话保持的时候我会再介绍。这里的核心原则是动态请求用 leastconn无状态短连接用 roundrobin需要会话保持时用 source 或者 cookie有缓存需求时可以考虑 uri 哈希。3.3 健康检查机制这一节最容易出问题健康检查是你整个实验环节里最先踩坑的地方。HAProxy 对后端的健康检查分为主动和被动两种主动检查由 HAProxy 定期发起探测后端的某个HTTP地址是否返回成功被动检查则是 HAProxy 在转发请求时根据后端连接失败或者超时的次数来决定是否临时摘除节点。配置里最常见的主动检查写法是这样server web1 192.168.1.11:8080 check inter 2000 fall 3 rise 2check表示对该后端启用健康检查inter 2000表示每2秒检查一次fall 3表示连续失败3次后将节点标记为不可用rise 2表示连续成功2次后将节点重新标记为可用。这三个参数强烈建议不要用默认值因为 HAProxy 默认的检查间隔是10秒恢复阈值也比较保守生产环境遇到后端服务重启时节点恢复可能要等好几十秒期间一部分请求还是会打到已经恢复一半的节点上造成 503 或超时。再强调一个细节健康检查的 URL 最好不要只写option httpchk而是明确指定路径比如option httpchk GET /health。后端应用应该提供一个专门用于健康检查的接口这个接口不能有复杂的业务逻辑也不能依赖数据库和缓存它只做一件事返回200证明进程是活的。我在生产环境里见过不少团队把健康检查打到首页上结果首页某个广告位接口超时导致前置机把整个节点摘掉这是非常典型的误报案例。4. 动手实验完整跑通一次负载均衡4.1 先让两台后端服务都动起来开始之前先在 Web1 和 Web2 上准备一个最简单的 HTTP 服务。如果你机器上有 Nginx直接用 Nginx 默认页也行但为了能清晰地看到请求到底落在了哪台机器上我建议把响应内容改成不同的标识。我用的方法是修改两个后端服务的默认首页分别打印出节点名称。比如 Web1 的页面内容是一个大标题“I am Web1”Web2 里是“I am Web2”。这样通过 curl 看到的返回内容马上就能判断分发效果。如果后端用 Python 快速起一个端口号为 8080 的服务命令如下# Web1 上执行 mkdir /tmp/site echo I am Web1 /tmp/site/index.html cd /tmp/site python3 -m http.server 8080Web2 上复制一份将内容改成 “I am Web2”端口不变。先用浏览器或者 curl 直接访问两台机器的 8080 端口确认服务是可用的再进入下一步。这个冗余步骤看着多余实际上能省掉后面大量的连带排查——我就见过有人后端服务根本没起来直接去配 HAProxy最后在那儿对着 HAProxy 的配置翻来覆去查了一整晚白白浪费时间。4.2 编写配置并启动 HAProxy回到 HAProxy 节点编辑/etc/haproxy/haproxy.cfg。我实验时用的是上面 3.1 节那份配置这里我把完整的版再贴一次方便你直接复制修改global log /dev/log local0 info maxconn 50000 user haproxy group haproxy stats socket /var/run/haproxy.sock mode 600 level admin defaults log global mode http option httplog option dontlognull retries 3 timeout connect 5000 timeout client 50000 timeout server 50000 frontend web_front bind *:80 default_backend web_servers backend web_servers balance roundrobin option httpchk GET / server web1 192.168.1.11:8080 check inter 2000 fall 3 rise 2 server web2 192.168.1.12:8080 check inter 2000 fall 3 rise 2配置写好后启动前一定要执行配置校验haproxy -c -f /etc/haproxy/haproxy.cfg如果输出Configuration file is valid说明语法没问题。这一步必做尤其在经历过文件里缺一个空格就导致整个服务无法启动之后你就知道这个习惯有多宝贵了。校验通过后用 systemd 启动systemctl restart haproxy systemctl status haproxy然后检查端口监听情况ss -lntp | grep haproxy。HTTP 模式下的 80 端口和可能配置的监控端口都应该处在 LISTEN 状态。4.3 验证流量分发和故障转移效果链路通了之后就可以观察流量分发效果了。在客户端机器上连续执行多次 curlfor i in {1..10}; do curl -s http://192.168.1.10/; done因为我用的是 roundrobin输出会交替出现 “I am Web1” 和 “I am Web2”。说明 HAProxy 正在按轮询算法把请求均衡地分发到两台后端整个实验的核心链路已经跑通。接下来测试故障转移这部分我觉得才是整个实验最精彩的地方。直接把 Web2 的 Python 服务停掉按 CtrlC 或者 kill 掉进程然后继续循环 curl HAProxy 的地址。前几秒你可能会看到偶尔还能请求到 Web2这是因为fall 3的参数意味着至少要连续失败3次才会摘除节点这是合理的设计不算 bug。等大约几秒钟后所有请求都会落到 Web1 上因为 HAProxy 已经检测到 Web2 不可用并自动把它从负载池里摘除了。这时候再看一眼 HAProxy 的运行状态如果你配置了 stats 页面能直观地看到 Web2 的状态变成了 DOWN。恢复实验的时候重新启动 Web2 的服务然后继续 curl 观察。因为设置了rise 2HAProxy 需要连续两次健康检查成功才会把节点加回池中。这一点在生产环境中非常重要它不是故意给你找麻烦而是为了防止后端服务刚起来还没完全就绪时就被塞入流量造成启动初期的大量报错。4.4 顺手把监控页配置了看得见的才放心配置 stats 页面是一件投入产出比非常高的事它能让你不用登录服务器就能看到后端节点的实时状态。在 haproxy.cfg 里追加一段 frontend 或者 listenfrontend stats_page bind *:8404 stats enable stats uri /stats stats refresh 5s stats auth admin:admin123保存后校验并重启 HAProxy。浏览器访问http://192.168.1.10:8404/stats输入用户名密码 admin/admin123 之后你就能看到一个表格页面里面包含了当前每个前端的会话数、每个后端的健康状态、请求成功率等关键数据。注意这里只是实验环境密码设置得简单无妨但生产环境千万不能用这种级别的密码还要加上 ACL 限制访问来源 IP。5. 进阶实验ACL分流与会话保持5.1 用 ACL 把静态请求分到指定后端基础实验跑通之后可以再往前走一步按照请求的特征把流量分到不同的后端。常见的需求是把图片、CSS、JS 这些静态资源请求交给专门的静态服务器动态 API 请求交给应用服务器。这就是 ACL访问控制列表分流。我在配置文件里加了一段frontend web_front bind *:80 acl is_static path_beg -i /static acl is_image path_beg -i /images use_backend static_servers if is_static or is_image default_backend web_servers这段配置的逻辑很容易读懂当请求路径以/static或/images开头时使用static_servers这个后端否则使用默认的web_servers。-i表示忽略大小写避免有人把/Static也当成动态请求处理。path_beg是路径前缀匹配。ACL 还有非常多匹配函数比如hdr(host)可以按域名匹配url_param可以按 URL 里的参数匹配src_ip可以按来源 IP 匹配。这些规则可以随意组合生产上的灰度发布、多环境切换底层基本都是这套 ACL 机制。实验本身并不复杂但理解这种“按需路由”的思想非常重要——你在这一节学会的 ACL 能力未来可以直接迁移到很多真实网关项目里。5.2 会话保持别再让用户“突然掉线”后端服务做负载均衡之后就面临一个新的麻烦如果用户登录之后下一次请求被分到了另一台后端服务器而这两台服务器的 Session 没有同步用户就会莫名其妙地掉线。解决思路有两个方向一个是在后端做 Session 共享另一个是让同一用户的请求始终落到同一台机器上这叫会话保持。HAProxy 实现会话保持最常见的方式是 cookie 会话保持backend web_servers balance roundrobin cookie SERVERID insert indirect nocache server web1 192.168.1.11:8080 cookie web1 check inter 2000 fall 3 rise 2 server web2 192.168.1.12:8080 cookie web2 check inter 2000 fall 3 rise 2HAProxy 会在首次响应中给客户端种一个名为SERVERID的 cookie比如值为web1。后续的请求只要带着这个 cookieHAProxy 就能直接根据 cookie 值把请求转发到指定节点不再参与轮询。indirect参数表示如果客户端请求中没有这个 cookie就正常按负载均衡算法分发nocache则告诉中间缓存设备不要把这个 cookie 缓存下来。还有另一种基于 source 的会话保持方式配置是balance source它根据客户端 IP 进行哈希计算同一个 IP 基本会分到同一台后端。这种方式不需要修改请求和响应但缺点也明显如果多个用户通过同一个出口 IP 访问流量会全部集中在某台机器上负载均衡效果大打折扣。所以我更推荐 cookie 方案它对用户无感知分发也相对科学。6. 常见问题与排查技巧实录6.1 后端明明活着却出现503 Service Unavailable这是新手最崩溃的场景直接访问后端 IP 正常通过 HAProxy 访问就 503。我用一个速查表来帮你按图索骥。排查项操作方法说明健康检查配置haproxy -c -f /etc/haproxy/haproxy.cfg先确认配置语法没问题后端服务监听在后端机器上ss -lntp确认 8080 真的在监听健康检查路径curl 后端的 /health 或 /确认检查接口返回2xx防火墙与网络在HAProxy上 curl 后端IP:端口确认两机网络通节点是否被摘除stats页面看后端状态如果显示DOWN原因见上行这里有一个非常容易忽略的细节option httpchk GET /里的路径必须与后端实际返回路径一致。很多框架默认对不存在的路径返回 404404 会导致检查失败节点被判定为不可用。所以我在实验里建议后端至少要有一个返回 200 的根路径或者干脆定义 /health 专用检查接口。6.2 高并发下连接数上不去先检查进程限制实验环境可能遇不到这个问题但你在测试自己写的压测脚本时就可能发现QPS 到了一定的值怎么也上不去。90% 的情况是 Linux 的进程文件描述符限制在作怪。在启动 HAProxy 的 systemd unit 文件里添加[Service] LimitNOFILE1048576同时配置里的maxconn不要超过系统实际能开的文件描述符数量。这个参数到底应该设多大我提供一个粗略的计算公式maxconn大约等于LimitNOFILE的三分之一因为每条连接不只是 HAProxy 与客户端之间的一条还有 HAProxy 与后端之间的一条再加上日志、监控连接每请求要消耗掉多个 fd。不然你设了个吓死人的maxconn 1000000实际系统连一万连接都开不出启动时不会报错但压力测试一上来立刻失败。如果你用的 HAProxy 版本在 1.8 以上还可以开启多线程来利用多核 CPU在 global 段加nbthread 4这也是生产环境性能调优的重要手段。需要提醒的是nbthread和旧版本的nbproc不要同时使用那是两代不同的进程模型混用会导致配置直接无法启动。6.3 日志为什么一片空白默认不输出得自己开HAProxy 默认情况下不输出像 Nginx access.log 那种日志除非你显式配置。排查问题、分析请求时没有日志几乎是寸步难行所以拿到手一定要先把日志打开。在global段里已经写了log /dev/log local0 info这是把日志交给本机的 syslog。Ubuntu 上还要确认 rsyslog 接收 UDP 日志。到/etc/rsyslog.d/下新建一个haproxy.conf$ModLoad imudp $UDPServerRun 514 local0.* /var/log/haproxy.log然后重启 rsyslog 和 HAProxy。之后再访问一次服务你会看到/var/log/haproxy.log里已经写入了完整的请求信息包括客户端 IP、请求方法、URL、响应码、后端处理时间等。日志里有几个关键字段值得你留意比如Tc表示后端连接建立时间Tr表示后端响应时间。如果Tr特别大说明问题出在后端应用而不是 HAProxy如果Tc大而Tr小则可能是网络或者后端连接队列拥堵。6.4 配置变更别用 restart学会优雅热加载最后再分享一个生产环境必备的操作习惯。当你修改了 haproxy.cfg不要用systemctl restart haproxy来做重载。restart 会断开所有现存连接哪怕是正在进行的请求也会被直接掐断这在生产环境是不可接受的。正确做法是haproxy -c -f /etc/haproxy/haproxy.cfg if [ $? -eq 0 ]; then systemctl reload haproxy; fireload实际上会启动一个新的 HAProxy 进程新的连接会交给新进程处理旧进程则把还在进行的连接处理完之后再自动退出。这种无缝切换保证了服务不中断是生产环境的标准操作。我踩过最大的一个坑就是当初不知道这个区别直接用 restart 发布了配置结果当时线上正有一批长连接在跑瞬间全部断连监控告警响成了一片那个狼狈劲儿到现在都还记得。写在最后这个实验还能往哪个方向走如果你按前面的步骤把这套实验完整走了一遍我相信你对 HAProxy 的理解已经超过了大半只会在官网抄配置的人。实际工作和学习过程中我现在养成的一个习惯就是无论做一个多小的实验都按生产标准对齐——该做的健康检查要细化日志要完整配置变更要高可用监控页面要有。这些小习惯单独拎出来都不起眼合在一起才是从“会配”到“会用”的分界线也是这套实验真正想教给你的东西。HAProxy 后续可以扩展的方向还挺多比如后端加一台 Redis 来测试四层 TCP 模式的负载均衡或者在前面配置 HTTPS 终结做全站加密再进一步可以研究它的 lua 脚本扩展用 HAProxy 实现简单的 API 限流和熔断。实验设备还在手边的话不妨直接动手尝试一下踩过的坑永远比看过的文档记得更牢。