
我折腾负载均衡这些年手边最顺手、最不敢乱动的工具就是HAProxy。不是说它简单恰恰是因为它把复杂的东西藏在简单的配置背后一旦你真把它跑透了它比很多商业负载均衡设备都扎实。这篇内容我就从实际落地的角度把这几年用HAProxy接生产流量的经验掰开揉碎讲一讲包括选型理由、核心转发原理、配置手感和排错思路给正在选型或者刚被分配去配HAProxy的朋友一个参照。1. 为什么我在一堆负载均衡器里最终固定用HAProxy先说选型。接触过不少团队一上来就在Nginx和HAProxy之间纠结还有人直接用云厂商的SLB一挂了事。我的态度是如果是四层大流量入口、TCP协议为主、需要极致转发性能HAProxy基本是首选如果主要场景是七层HTTP并且你还要在同进程里处理静态文件、做缓存那Nginx更顺手。HAProxy的优势集中在几个地方。第一是性能。HAProxy是纯代理它不干缓存、不解析复杂的业务逻辑专心做转发。它用单进程加事件驱动的模型现在支持多线程在Linux上用epoll转发吞吐和并发连接数都非常能打。我压测过同样的机器HAProxy扛十万级并发连接比Nginx轻松很多内存占用也小。CPU基本只花在报文解析和连接管理上。第二是四层和七层的统一支持。HAProxy的mode tcp和mode http能覆盖绝大部分场景。很多内部微服务接口走TCP比如自定义RPC、Redis集群前置代理用HAProxy做四层负载非常顺手对外HTTP API又可以用七层模式做路径路由、请求头改写、ACL调度。一套配置管两种协议省事。第三是健康检查和故障摘除的精细度。它内置的HTTP检查可以精确到返回码、甚至可以检查响应内容还能设置检查间隔、升降级阈值。真实场景里我能让后端某台机器在连续三次失败后被摘除在连续两次成功后恢复弹性和可控性比云SLB的固定策略舒服得多。第四是配置灵活性和运行时管理。HAProxy可以做到零停机修改配置——通过admin socket动态添加、删除、禁用后端服务器排障的时候可以临时把某台机器摘掉不用重启进程。这个操作对生产环境太重要了。当然我也要说它的局限。HAProxy默认不做缓存有cache模块但比较简陋、不终结TLS时不做证书自动化管理、不处理WebSocket之外的高级HTTP特性。需要这些能力的场景我会在它后面再加一层别的服务或者配合Nginx做互补。选型不能只看一台工具的单项优点要看整个链路的组合。用HAProxy最大的门槛不是安装而是理解它的配置模型和转发模型。一开始我也把后端服务器直接写在defaults里配到一半发现请求全部打到同一台机器上后来才明白frontend和backend是两个逻辑段必须在frontend里用use_backend显式路由。这个理解到位之后配置就像搭积木一样清晰了。2. HAProxy的核心转发原理进程模型、代理模式和一个请求的生命周期先说进程模型。老版本HAProxy是单进程单线程靠epoll事件循环处理所有连接和IO事件。从2.x开始支持多线程配置nbthread可以指定线程数每个线程有自己的事件循环和连接缓存。线程之间基本无锁或者细粒度锁所以扩展性不错。实际使用中我很少刻意把它配成多线程因为单线程在中等流量下已经足够多线程反而要注意CPU亲和性、锁竞争和日志顺序。如果你机器核心数很多、流量也很猛可以试试nbthread 4配合cpu-map但一定要压测验证不要想当然。再说多进程和多线程的取舍。早期版本用nbproc跑多进程每个进程独立监听、独立做负载均衡。现在官方推荐用多线程替代多进程因为多进程之间共享不了内存状态、健康检查状态也不好统一而且fork之后fd的管理容易出问题。新项目我建议直接用nbthread不要再走nbproc的老路。然后是转发模型。HAProxy接收一个请求本质上是做了两段连接客户端到HAProxy的入站连接以及HAProxy到后端服务器的出站连接。它不直接把socket转交而是把数据从一个连接搬到另一个连接。四层模式下它做的是TCP stream转发——收到客户端数据就原样转发给后端后端返回的数据再原样回给客户端中间不做任何内容修改所以延迟极低。七层模式下它解析HTTP请求头方法、URI、Headers转发前可以改写、增加请求头还可以根据ACL选择不同的后端组再根据负载算法挑一台具体服务器。一个HTTP请求的生命周期大致是这样客户端连上HAProxy的frontend监听端口三次握手完成之后HAProxy根据配置的绑定和规则决定走哪个backend然后向后端发起新的TCP连接转发HTTP请求等后端返回响应再把响应传回客户端。如果启用了连接复用http-reuse同一个后端连接可以被多个客户端连续复用大幅降低后端的建连压力。这也是HAProxy在七层场景性能好的一个重要原因——它把连接复用做到非常细既照顾了客户端延迟又减轻了后端负载。这里有个重要的调优点和坑连接复用。默认配置里很多模板会把timeout server设得很大甚至不设这样连接一直挂着后端机器上会堆很多空闲连接。我的习惯是timeout server和timeout connect分开控制connect一般1-5秒server设置30-60秒看业务需求。如果你有长轮询或流式接口单独用timeout tunnel或者把该后端的超时放宽不要干所有后端都用一个统一的超时配置。七层模式下还要注意keep-alive的开关。HTTP/1.1下客户端默认是keep-alive如果HAProxy关掉了keep-aliveoption httpclose那每个请求都得重新走三次握手点击页面上每个资源的耗时肉眼可见地增加。我见过有团队为了省连接数把httpclose开着结果客户端体验变差、后端CPU也上去了因为握手开销全算在后端上。正确的做法是让客户端到HAProxy保持长连接HAProxy到后端做连接复用用option http-server-close或http-reuse来管理后端连接不要一刀切全关。3. 从零到一一套能上生产的HAProxy基础配置长什么样直接上一份我常用的基础配置然后逐段解释为什么这样写。global log stdout format raw local0 info maxconn 100000 nbthread 4 cpu-map auto 1-4 0-3 tune.ssl.default-dh-param 2048 ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 stats socket /var/run/haproxy.sock mode 600 level admin defaults mode http log global option httplog option dontlognull timeout connect 3s timeout client 30s timeout server 60s timeout http-request 5s timeout http-keep-alive 2s retries 2 maxconn 20000 frontend fe_web bind *:80 bind *:443 ssl crt /etc/haproxy/certs/site.pem http-request set-header X-Forwarded-Proto if { ssl_fc } # 静态资源直接进静态后端 use_backend be_static if { path_beg /static/ } # 健康检查接口单独处理 use_backend be_health if { path_beg /healthz } default_backend be_api backend be_static balance roundrobin server s1 192.168.1.11:8080 check inter 3s fall 3 rise 2 server s2 192.168.1.12:8080 check inter 3s fall 3 rise 2 backend be_api balance leastconn option httpchk GET /healthz http-check expect status 200 server a1 192.168.1.21:9000 check inter 5s fall 3 rise 2 server a2 192.168.1.22:9000 check inter 5s fall 3 rise 2 backend be_health mode http server local 127.0.0.1:18888 checkglobal段里我开启stats socket这是我在生产里离不开的功能。有了它我用socat或echo就能实时查看和操作运行状态比如动态启用、禁用后端机器查看计数器甚至修改部分运行参数而不用重启。它的安全等级我设成adminsocket权限600只允许root和haproxy用户访问不暴露到外网。defaults是配置的地基。我强烈建议把通用参数放defaults然后在frontend和backend里只覆盖特殊部分减少重复。timeout是这里最关键的几行。connect 3s表示HAProxy在后端建立连接的超时server 60s表示如果后端60秒没有任何数据返回就断开client 30s表示客户端如果30秒不传数据就断开。这几个值要按业务调整比如文件上传接口client时间要放大几个级别。balance是我几乎每套配置都会单独指定的。be_static用的是roundrobin因为静态资源每个请求耗时就几十毫秒轮询最公平。be_api用的是leastconn因为API请求时长差异非常大有的几百毫秒有的几秒按连接数调度更合理。这就是负载均衡选算法的核心均匀轮转适合短小一致的请求最少连接适合流量波动大、处理时间差异大的场景。健康检查配置是生产稳定的核心。inter 3s表示每3秒探测一次fall 3表示连续3次失败判为宕机rise 2表示连续2次成功判为恢复。我把静态后端探测间隔设3秒API后端设5秒原因是API后端本身有复杂依赖太快地频繁探测会增加不必要的压力。be_api里我用了option httpchk GET /healthz并expect status 200这是七层健康检查能保证接口真的能返回200才把流量打过去。有些团队只用TCP connect检查端口通就算健康结果接口实际已经500了还在往里打流量用户看到的就是一片报错。所以能用七层检查绝不用四层。这份配置里还有个小技巧把healthz路径单独分到be_health这样可用性探活就不会污染业务后端的统计和日志。这个请求是负载均衡器或者云监控定期来打的一旦混在业务请求里会影响你的容量评估和调用链追踪。实际落地时还需要考虑TLS终止的证书路径。上面的bind *:443 ssl crt指定的是pem格式证书文件。我一般会把公钥、私钥、中间证书链拼成一个pem放到/etc/haproxy/certs/下证书轮换时直接用socat命令热更新。如果你有多域名证书可以考虑crt和crt-list的组合新增域名无需重启进程。4. 性能调优的实践心得从连接数到内存占用调优要有基准没有压测数据的调优都是自己骗自己。我调优HAProxy的步骤是先记录操作系统初始配置再逐项修改和压测每次只改一个变量记录吞吐量、延迟、CPU、内存。先看操作系统层。HAProxy对文件句柄数非常敏感因为每个连接至少占一个fd。在高并发场景下ulimit和内核单进程fd上限必须放开。通常在systemd service里设置LimitNOFILE1048576同时内核参数net.core.somaxconn和net.ipv4.tcp_max_syn_backlog也要相应提高因为这些参数控制SYN队列长度。如果这些队列满客户端握手会超时表现为连接建立缓慢或大量connection timed out。TCP层有几个参数对HAProxy转发性能影响很大。net.ipv4.tcp_tw_reuse和tcp_fin_timeout能加速time_wait状态socket的回收减少端口占用。net.ipv4.ip_local_port_range决定了作为客户端发起后端连接时可用源端口范围如果你的后端连接是短连接且客户端访问量大这个范围小了会出现Cannot assign requested address。我一般设成1024 65535。net.ipv4.tcp_max_tw_buckets也要关注它限制time_wait总数超过后新连接会被拒绝。然后是HAProxy自身参数。maxconn是全局最大并发连接数这个值要综合内存计算。HAProxy每个TCP连接的内存在2.x下大概占用几十KB100万连接意味着大约需要几GB内存。我一般按1G内存约1.5万个连接来粗略估再结合机器实际业务调整。maxconn设太大没有意义内存耗尽是致命的设太小则流量高峰直接连接失败可以通过监控日志里的maxconn达到率来判断是否需要调整。tune.bufsize和tune.ssl.maxrecord影响七层场景的缓冲区大小。tune.bufsize默认16KB如果接口响应体很大可以适当提高到32KB减少多次系统调用的开销。但要注意改缓冲区会增大内存占用因为并发每个连接都要分缓冲区不要无脑拉大。tune.ssl.maxrecord控制TLS记录大小调大可以减少TLS分片增加吞吐但太大额会提升单连接内存占用这个一般用默认就好不做特种优化不用动。线程调优方面我一般nbthread设成和物理核心数一致或者稍小一点然后用cpu-map绑定到固定核心远离那些被系统中断和网络驱动占用的CPU。注意有超线程的机器逻辑核跟物理核要区分开不然两个线程绑到同一物理核反而互相干扰。绑定后测试一下如果吞吐没有提升大概率是单线程已经够用了这时候多线程只会增加锁竞争。还有一点经常被忽略accept锁。当nbthread大于1时多个线程会竞争accept新连接的锁高并发下锁竞争会成为瓶颈。所以如果连接建立频率极高且总并发不高单线程可能反而更快。我见过压测环境里双线程比单线程吞吐还低的案例就是因为accept锁开销。正确的做法是压测一个上线范围找到你自己的甜点值。调优的最终指标要看尾部延迟。HAProxy 2.x提供了log-asap、option logasap等机制可以提前记录log时间戳配合%Tr等变量可以在日志里算出请求到尾端的处理时间。我压测时必看P99延迟不只看平均。有时候平均延迟是50msP99是500ms说明长时间请求大量分布在尾部很可能有资源争抢或长尾请求拖累这是需要进一步定位的信号。5. 排错实战连接卡住、后端假死、请求超时的完整排查链路HAProxy的坑大部分不是它坏了而是我们看到的现象根本不在它这层。我排错的思路一直是先在HAProxy这一层定位连接和字节到底有没有流动再判断业务端到底发生了什么。这里放几个真实遇到过的案例思路。第一类现象客户端请求偶发超时服务端日志没收到请求或者收到了但响应很慢。排查链路是先看HAProxy日志和计数器。日志里如果timeout server出现在后端响应之前说明HAProxy已经建立连接并转发了请求但等待后端响应超时了。这时候去后端看是否线程池耗尽、是否有慢SQL/慢外部调用把请求线程堵死。常见原因是后端连接池配置过小并发高时请求排队。我遇到过Java后端默认连接池线程数设很低并发一上来请求全排队HAProxy日志一片timeout server。第二类现象health check间歇性失败后端监控却显示服务正常。这种情况先看检查间隔和阈值inter 3s表示3秒探一次后端如果因为垃圾回收暂停超过3秒JVM Full GC检查就会失败连续3次后摘除。摘除后流量分流到其他机器可能其他机器也受影响出现雪崩。处理方法后端把GC停顿降到最低HAProxy侧把inter调大一点比如5-8秒把fall调成5-6次给后端更多喘息时间同时可以打开graceful shutdown机制让HAProxy能感知后端主动下线信号。七层健康检查本身比较消耗加上expect status 200会导致每次探活都打一次完整API这个频率要控制好。第三类现象连接数打满日志里大量maxconn reached。先用admin socket看当前连接数和后端各连接的分布。show info里的CurrConnsshow errors里的错误数show servers state里的权重和状态这些都能帮忙定位。常见原因有两个一是业务突发流量超过预期二是某台后端出了问题连接全挤在健康后端上导致单机连接数暴涨。方案是扩大集群、调低单机maxconn并设置更合理的健康检查把故障摘得快一点、同时把三台机器的权重调整一下避免流量倾斜。第四类现象连接一直在SYN_SENT客户端连接建立不成功。这个大多数不是HAProxy的问题而是系统网络参数或防火墙。排查命令是ss -s看socket状态ss -antp | grep haproxy_worker_pid看连接堆积在哪个队列再用netstat看监听队列是否有溢出。如果大量连接在SYN queue或者accept queue说明HAProxy的monitor层已经把队列占满了调整somaxconn、tcp_max_syn_backlog或者为监听端口设置accept-proxy之类的特性来减少等待。注意如果HAProxy自身没有全连接能力后面即使调整也没辙需要看资源上限。有一次印象很深的排错经历客户端定时请求偶发504后端完全看不到任何请求。进入HAProxy看日志发现前端到后端的连接建立成功但HTTP请求头没有完整到达HAProxy——也就是说客户端发了半个请求就不传了。最后发现是业务方在客户端侧设置了很短的写超时网络稍有抖动写一半就断HAProxy还在傻等完整的请求头。我调整了timeout http-request这个值默认是10秒但对某些弱网客户端来说不够调大到30秒后故障消失。这提醒我七层超时参数要结合用户的实际网络环境不能拍脑袋。6. 从手动到自动HAProxy的运维和监控落地手工用admin socket管理HAProxy没问题但一个团队不能只靠人工。我落地监控的经验分三块。第一块是采集指标。HAProxy在2.0之后提供Prometheus exporter能力在global段启用相关配置或者使用haproxy_exporter。核心指标我至少盯这些haproxy_process_connections_total总连接数、haproxy_server_current_queued队列积压、haproxy_server_current_sessions当前会话数、haproxy_backend_http_responses_total按状态码聚合的崩坏率。需要注意的是2.x之后直接用haproxy -f haproxy.cfg能读到内置的统计信息不用额外装exporter。promex那段配置我习惯放在一个独立的socket甚至单独的进程里防止监控采集本身干扰转发。第二块是健康检查和自愈。HAProxy本身只管摘除不会拉起所以我把健康检查结果接入告警发现某台后端连续掉线立即通知负责人并自动拉起服务。拉起的操作有一个注意点不能在服务能在300ms内快速启动的情况下自动拉起否则健康检查刚摘除又恢复频繁抖动反而容易造成更大风险。我的习惯是给恢复加上冷静时间检查失败后至少等30秒再去拉起拉起后等2-3个健康检查周期确认稳定了再放流量。第三块是配置管理。我建议HAProxy配置用版本化管理改动走代码评审不要在生产环境直接改。每次上线前用haproxy -c -f haproxy.cfg做配置校验。生产热更新的标准做法是先修改配置文件然后通过admin socket执行reload命令或者直接haproxy -sf old_pid优雅重启这样HAProxy会fork新进程等老进程的连接结束后再回收。优雅重启比kill -HUP更安全因为HUP在某些内核版本下可能丢连接。日志安全也别忽视。HAProxy日志里有真实客户端IP、UA、请求路径这是一笔资产。我在全球标准下用option forwardfor获取真实IP放到日志里做访问审计同时在采集端脱敏掉敏感字段。日志最好输出JSON格式方便后面接日志分析平台。7. 多套集群同步与配置漂移控制生产环境只要有两个以上HAProxy节点就一定会遇到配置漂移问题。我给所有节点做了配置一致性校验从版本库拉取配置统一生成校验和。别的工作不说至少保证每台负载均衡器上的haproxy.cfg是完全一致的——哪怕某个参数差异只有一毫秒的超时在多节点架构下也会导致流量调度不均匀。实际做法是用一个分发脚本把配置推送到各节点推送后自动reloadreload前后分别计算校验和不一致就回滚。这个脚本很简单但稳定性很好。我还在reload命令前后加了缓存如果配置根本没变化就不触发reload避免无意义断开。需要提到的是HAProxy的-p /run/haproxy.pid配合优雅重启必须在多节点方案里留好pid文件不然运维脚本根本没法控制新老进程交接。配置漂移的隐患不只是参数不一致还有证书文件不一致。TLS证书时间的轮换我也有统一脚本从证书管理平台拉取新证书生成pem分发到每台HAProxy通过admin socket的set ssl cert命令动态更新不用reload。reload期间TLS握手会瞬断但对某些业务来说是难以接受的。不能用admin socket更新的场景再考虑优雅重启。动态更新TLS证书还有一点set ssl cert之后需要关注证书是否完整替换包含私钥和链如果pem拼错会导致握手失败所以脚本里要有证书检查步骤包括openssl x509检查有效期和匹配私钥是否一致。8. 测试和上线怎么压测、怎么灰度、怎么回滚最后这部分直接关系到生产敢不敢把流量交给它。压测要按真实流量比例来不要只压并发。我压测时至少准备这几类请求纯HTTP GET短请求、POST长请求、WebSocket长连接、HTTPS握手。长连接对后的连接复用冲击很大最高压测时一定要注意后端机器CPU和连接数的水位。压测工具有几个选择ab适合简单GET压测wrk适合测吞吐和延迟分布h2load适合HTTP/2压测gatling适合复杂业务链路。我自己最常用wrk加一段Lua脚本模拟真实业务请求头比如模拟线上客户端带的auth header和trace id。压测前把HAProxy日志的accesslog级别提高压测结束后统一看日志统计数据尤其是P50/P95/P99延迟和timeout次数。灰度上线有多重办法。最轻量的是修改权重set weight server 128之类把某台机器权重设为1观察日志里是否只有极小比例流量落在它上面。验证合格后再逐步把权重加上去。更完整的是ACL化灰度在frontend里加一个use_backend根据特殊Header或Cookie路由到灰度后端。比如客户端带X-Canary: 1就切到新后端其他流量走老后端。整套灰度做完再把配置固化到正式版本发完后再清掉灰度规则保持配置干净。回滚预案绝对不能省。我上线新配置前必做三件事备份当前配置和证书记录当前进程PID和内存使用确认admin socket可连可操作。如果新配置上线后指标异常第一反应不是reload一次完事而是先看HAProxy进程是否在新老进程状态是否正常再根据异常是连接层、延迟层还是业务层决定是回滚配置、摘流量还是切备用集群。回滚配置时用版本管理的好处就出来了一条git checkout带回上一版reload后观察指标不回恢复再降级到备用集群。压力测试还经常暴露一个隐性坑本地连接数上限。很多操作系统默认的ulimit -n只有1024跑压测时客户端本身先被打爆了。开压测之前一定先检查压测机器的fd限制否则根本压不出真实数据。这个和HAProxy本身无关但只要踩过一次效率损失极大。总结一下个人体会HAProxy能稳定扛生产流量不是因为它有多宏大是因为它把一件事做到极致——连接管理和转发。你用得好不好取决于你把原理理解得多透、配置细节抠得有多细。上面的经验都是我实际跑过、压过、排过错才沉淀下来的希望能让正在用或者正要考虑用HAProxy的朋友少走弯路。特别是线上出了问题别急着重启先看日志、看计数器、看连接状态绝大多数故障都能在HAProxy这一层定位出方向。