HAProxy调度算法全解析:从roundrobin到leastconn的实践指南

发布时间:2026/9/9 4:35:22
HAProxy调度算法全解析:从roundrobin到leastconn的实践指南 我做了三年多的负载均衡运维真正把 HAProxy 的调度算法用明白是在一次线上事故之后。当时一个商城的接口服务用默认的 roundrobin 做了三台后端平时看着流量挺均匀结果一到秒杀场景其中一台机器 CPU 直接打满另外两台连一半都没用上。排查到最后发现问题不是出在机器配置上而是调度算法选错了。自那以后我对 HAProxy 的调度算法就格外较真。这篇内容就围绕 HAProxy 的调度算法展开把静态调度和动态调度的常见算法挨个拆开讲清楚包括它们的工作原理、适用场景、配置写法以及我在实际运维中踩过的坑。不管你是刚开始接触 HAProxy还是已经在生产环境跑了一段时间只要想搞清楚“请求到底是怎么被分到后端的”这篇文章应该都能帮到你。1. 调度算法到底在解决什么问题1.1 从一次请求分发说起先还原一个最简单的场景你配置了一台 HAProxy 作为反向代理后端挂着三台 Web 服务器用户在浏览器里输入域名请求到达 HAProxyHAProxy 需要决定把这条请求交给三台中的哪一台。这个“决定交给谁”的过程就是调度算法在做的事。听起来简单但这里面隐藏着好几个问题。第一三台机器的配置可能不一样有的 8 核 16G有的 4 核 8G如果平均分流量配置高的机器就浪费了。第二有些请求本身“重”比如上传文件、导出报表有些请求本身“轻”比如查询一条记录如果只按请求次数分发不考虑请求的实时负载就会出现某台机器被重请求拖垮的情况。第三用户的登录状态、购物车数据可能缓存在某台后端上如果每次请求都被分到不同机器用户可能刚登录完下一秒就被踢出去了。调度算法要解决的核心问题就是在这些互相制约的条件里找到一个平衡点既能最大化利用后端资源又能保证服务质量还能尽量满足会话保持的需求。1.2 两类算法的分水岭静态 vs 动态HAProxy 官方把调度算法分成两大类静态调度算法和动态调度算法。静态调度算法在配置加载时就确定了分发规则运行过程中不感知后端的实时负载状态。比如 roundrobin 在配置加载时会按权重把后端服务器排成一圈然后挨个轮转不管后端当前处理了多少请求、响应有多慢它都按既定的节奏走。相对的动态调度算法在每次请求到来时都会检查后端的实时状态比如当前连接数、响应时间然后基于这些动态数据做决策。这两类的选择没有绝对的好坏只看场景。静态算法胜在速度快、开销小适合后端性能差异不大、请求处理时间相对稳定的场景动态算法适合后端性能差异大、请求处理时间波动大的场景它能通过实时反馈把流量引向更空闲的机器。2. 静态调度算法拆解2.1 roundrobin最常用的轮询但不是万能药roundrobin 是 HAProxy 默认的调度算法也是绝大多数人第一次接触 HAProxy 时用的第一个算法。它的工作原理可以理解为“按权重做轮转”。假设你有三台后端权重分别是 1、2、3HAProxy 会把请求按权重比例分发处理完一轮后重新开始。这里需要注意HAProxy 的 roundrobin 是“平滑加权轮询”不是简单的先分给权重大的、再分给权重小的。它会在一个周期内尽量让分发顺序均匀交错避免某台机器在短时间内被连续集中请求。举一个直观的例子说明。三台后端 A、B、C权重 1:2:3按平均轮询的思路很多人以为顺序是 A、B、B、C、C、C然后再循环。但平滑加权轮询会把这个顺序打散实际顺序可能是 C、B、C、A、C、B、C这样每一台机器的请求到达间隔更均匀不会出现 B 刚处理完两个请求马上又来一个的情况。这个算法最大的优势是配置简单、实现稳定在后端配置均衡、请求处理时间差异不大的场景下基本不需要额外调优。但我自己在实践中发现它有一个先天性短板不感知后端的实时负载。如果某一台机器因为代码 Bug 或者外部依赖变慢导致响应时间暴涨roundrobin 依然会把等量的新请求分给它直到健康检查把它摘掉。实际配置长这样backend web_servers balance roundrobin server web1 192.168.1.11:8080 weight 1 server web2 192.168.1.12:8080 weight 2 server web3 192.168.1.13:8080 weight 3如果后端机器性能差异不大可以把权重都设为 1让 HAProxy 均匀分发。2.2 static-rr没有动态调整能力的轮询变体static-rr 和 roundrobin 非常像也是按权重轮询分发唯一的关键区别是static-rr 不支持在运行时通过管理接口调整权重。如果你用 socat 或者命令行向 HAProxy 发送指令去修改某台服务器的 weightroundrobin 可以即时生效static-rr 不会响应。这个差异在什么场景下重要呢我举个实际案例。之前给一家电商公司做流量压测压测过程中发现某台后端机器的性能瓶颈比预期出现得早我通过管理接口把它的权重从 1 调低到 0.5让流量少往那台机器上打。这个操作在 roundrobin 下能立刻生效但如果配置写的是 static-rr就必须重新加载配置或者重启 HAProxy 进程。所以在需要动态调整权重的场景下roundrobin 明显优于 static-rr。static-rr 唯一的优势是它比 roundrobin 更节省 CPU 开销但这点开销在现代服务器上几乎可以忽略。我的建议是如果没有特殊理由直接用 roundrobin 就够了static-rr 适合那些后端权重策略极其固定、完全不打算在运行时做任何调整的静态环境。配置写法backend web_servers balance static-rr server web1 192.168.1.11:8080 weight 1 server web2 192.168.1.12:8080 weight 12.3 first谁的槽位空就填谁first 这个算法和前面两种思路完全不同。它的策略是从第一台后端开始检查只要这台后端的连接数还没达到配置的 maxconn就把请求分给它只有当第一台满了才继续往后面的后端分发。这个算法对应的场景非常明确你想让第一批服务器尽可能被填满把空闲的服务器留作“预备队”。比如你配置了 5 台后端正常情况下只想让前 2 台处理流量后面 3 台留着做横向扩容的缓冲。当业务流量突然上涨前两台达到 maxconn 后第三台、第四台会自动开始承接流量。first 算法用的不多但在特定业务里很香。我见过一个比较典型的用法是在备份系统里主处理节点优先承接所有任务只有主节点忙不过来时才把任务交给从节点。还有一个场景是使用热备架构时希望首选服务器始终处理流量其他服务器作为热备等待。需要注意的是如果你配置了 maxconn 0意味着后端没有连接数上限这种情况下 first 算法会把所有请求都发给第一台服务器后面的永远不会被用到。配置写法backend workers balance first server worker1 192.168.1.21:8080 maxconn 100 server worker2 192.168.1.22:8080 maxconn 100 server worker3 192.168.1.23:8080 maxconn 1003. 动态调度算法拆解3.1 leastconn把新请求交给最闲的机器leastconn 是我在生产环境里用得最多的动态调度算法它的分发逻辑很简单每次请求到来时检查所有后端当前的连接数把请求分给当前活跃连接数最少的那台。这个算法能弥补 roundrobin 感知不到实时负载的问题。比如同样是三台后端其中一台正在处理几个耗时很长的导出请求连接数一直维持在 5 个左右另外两台连接数是 0。此时新进来的请求如果走 roundrobin可能会被分到那台已经积累了长任务的机器上但如果走 leastconn新请求会被分到连接数为 0 的机器上等那台忙完再承担新请求。我在实践中发现leastconn 最适合的场景是请求的处理时间长短差异很大而且后端连接数能真实反映机器压力。比如数据库代理、消息队列消费者、文件转换服务这类场景每个请求的耗时可以从几十毫秒到几十秒不等如果用 roundrobin 就会出现严重的负载倾斜。配置写法backend heavy_workers balance leastconn server worker1 192.168.1.31:8080 server worker2 192.168.1.32:8080 server worker3 192.168.1.33:8080但 leastconn 也有需要注意的地方。它会对请求进行排队等待如果后端使用长连接并且每台后端都一直保持大量连接比如连接池模式那每台机器的连接数差别不大leastconn 的效果就不明显了。另外一个问题是leastconn 只看当前连接数不感知 CPU、内存、磁盘 IO 这些更底层的负载指标所以如果“重请求”和“轻请求”的差异非常大连接数也不一定能准确反映真实负载。这种场景下可以配合后端的健康检查级别比如通过自定义检查脚本返回不同的负载水位来弥补。3.2 random随机并非坏事反而更平滑random 算法是 HAProxy 2.0 引入的实际上 HAProxy 内部把 random 分成了 random加权随机和 random均匀随机两种模式。在不指定权重的情况下它是均匀随机每个后端被选中的概率相同如果指定了权重则按权重比例进行随机选择。很多人觉得随机调度不靠谱但它在某些场景下反而比轮询更平滑。roundrobin 虽然是均匀轮询但当请求数较少时它的分布看起来会“一板一眼”比如 10 个请求按权重 1:2:3 分总是那几台机器轮流接。而 random 在请求量较小的时候分布更自然不会出现短时间内的规律性集中。random 还有一个在微服务场景下的独特价值配合长连接网关时它可以减少“惊群效应”。当新的连接请求到达时轮询算法总是按固定顺序分发容易让某台机器先接收一批连接而随机算法会把新连接分散到不同机器上让每台机器的连接建立节奏更错开。配置写法backend micro_services balance random server svc1 192.168.1.41:8080 weight 2 server svc2 192.168.1.42:8080 weight 3我自己的使用习惯是如果后端是典型的无状态服务请求处理耗时不长但实例数量经常变化比如 Kubernetes 里自动伸缩的 Podrandom 是个不错的主选方案。它不需要保存任何状态也不依赖当前连接数实现非常轻量。3.3 uri 与 url_param让同一个资源稳定落在同一台机器uri 算法是基于请求 URI 做哈希计算把哈希值映射到后端服务器上。它的核心特性是只要请求的 URI 不变就会被分到同一台后端。这个算法在做缓存服务时特别好用。我在给一个图片服务做负载均衡时后端挂了 4 台 nginx 做文件缓存如果同一张图片的请求被分散到不同机器上缓存命中率就会降低回源次数增多后端存储压力变大。使用 uri 算法后相同路径的请求永远落在同一台机器上缓存命中率从 40% 提升到了 75% 左右。uri 算法有一个可选参数用来指定对 URI 的哪一段做哈希。默认情况下是计算整个 URI 的哈希值包括路径和后面的参数。但有些场景下你需要忽略掉查询字符串。举个实际例子一个商品的路径是 /product/12345?trackingabc另一个请求是 /product/12345?trackingxyz如果包含完整 URI 做哈希这两个请求可能被分到不同后端但如果你希望同一个商品 ID 的请求都落到同一台机器就需要指定只对路径部分做哈希。配置写法backend cache_servers balance uri server cache1 192.168.1.51:8080 server cache2 192.168.1.52:8080 server cache3 192.168.1.53:8080 server cache4 192.168.1.54:8080url_param 则更进一步它指定从 URL 查询参数里取某个 key 的值来做哈希。比如你的 URL 是 /order?userId10086配置了balance url_param userId那么所有 userId10086 的请求都会分到同一台后端。这在做用户维度会话保持时非常有用不需要维护 session 表只需要让同一个 userId 的请求固定在同一个后端。配置写法backend user_services balance url_param userId server svc1 192.168.1.61:8080 server svc2 192.168.1.62:8080但 url_param 有一个明显的局限如果请求 URL 里没有携带你指定的参数HAProxy 会退回到普通的 roundrobin 算法。这一点很容易被忽略生产环境里经常出现“明明配了 url_param为什么流量还是不均”的问题检查一下日志和请求 URL 就会发现一部分请求根本没带那个参数。3.4 hdr按请求头做定向分发hdr 算法基于 HTTP 请求头做哈希。最常用的场景是按 Cookie 做会话保持或者按用户自定义的请求头做灰度发布。配置方式是在 balance 关键字后面指定头名称比如balance hdr(User-Agent)会按用户的 User-Agent 值做哈希balance hdr(Cookie)按 Cookie 值做哈希balance hdr(X-Client-ID)按自定义头做哈希。我用 hdr 做灰度发布的经验值得分享。当时我们上线了一个新版本服务想先让一部分企业客户试用配置了两组后端一组是稳定版一组是灰度版。我在请求里约定了一个自定义头X-Tenant-ID通过 hdr 算法让某些租户的请求固定打到灰度版。这样做的好处是不需要改 DNS不需要改 Nginx 规则只需要在客户端或者网关层加上这个头就能实现精准的灰度流量控制。但 hdr 有一个容易踩坑的细节如果请求头不存在或者请求头值的变化非常少哈希分布会非常不均匀。比如你按hdr(User-Agent)做哈希但这个系统里大部分请求都是同一个客户端程序发出的User-Agent 全都一样那所有请求都会被分到同一台后端其他的机器全都闲着。所以使用 hdr 前一定要确认你选的头的取值是否足够分散。另外 hdr 还有一个参数可以调整匹配模式比如balance hdr(X-ID)默认是完整匹配如果你希望从头值中间取一部分做哈希需要配置balance hdr(X-ID),word(2)这类与样本提取相关的用法。这些高级用法在日常工作中不常用但遇到前端传值格式不统一时会救你一次。3.5 source四层场景下的会话保持利器source 算法基于源 IP 做哈希计算把来自同一 IP 的请求固定分到同一台后端。这个算法在纯 TCP/UDP 四层转发场景下不可或缺。举个最常见的场景你的后端是一组 Redis 或者 Memcached 缓存节点客户端通过 HAProxy 访问如果来自同一台应用服务器的请求被分到不同的 Redis 节点缓存会反复失效。使用 source 算法后同一台应用服务器的所有请求都会落到同一个 Redis 节点上缓存命中率就有了保障。配置写法frontend redis_front bind *:6379 default_backend redis_back backend redis_back balance source server redis1 192.168.1.71:6379 server redis2 192.168.1.72:6379source 算法也支持指定哈希函数。默认是取源 IP 的完整地址做哈希但在某些场景下你可能希望只取 IP 的前三段做哈希这样来自同一网段的请求会集中到同一台后端。这个场景通常用于多出口网络的会话保持配置是balance source 255.255.255.0掩码后面的部分会被忽略。还有一个细节需要提醒source 算法在后端节点增加或减少时会导致大量请求重新映射造成缓存雪崩。如果后端是缓存节点扩容时需要谨慎操作或者使用一致性哈希的变体。4. 算法选型实战从场景到配置4.1 不同业务场景怎么选算法选择算法没有唯一正确答案但有一个清晰的决策路径。我会按下面这个思路来快速判断先把业务分成两类。第一类是无状态服务请求之间没有关联后端不保存会话数据。这种场景下后端性能均衡选 roundrobin后端性能不均衡选 leastconn后端节点频繁变化选 random。第二类是有状态服务需要会话保持。如果是 HTTP 会话保持优先考虑 hdr(Cookie) 或 url_param如果是四层连接保持优先考虑 source如果是同一资源需要固定到同一台后端比如文件缓存优先考虑 uri。下面是一个直观的对照表业务场景推荐算法选择理由无状态 Web 服务性能均衡roundrobin实现简单分发均匀无状态服务请求耗时差异大leastconn实时感知连接数避免长任务堆积微服务网关实例频繁扩缩random无状态平滑度高HTTP 文件/图片缓存uri相同 URI 落在同一台提升缓存命中率用户会话保持HTTPhdr(Cookie) 或 url_param按用户标识固定后端数据库/Redis 四层访问source源 IP 哈希保持连接一致性主备模式优先用满主节点first未达到 maxconn 前不用备机动态调整后端权重roundrobin支持运行时调整权重这个表不是我拍脑袋写的而是基于实际运维经验归纳出来的。大家在实际使用中可以先按这个表选型跑一段时间后观察后端的连接数、CPU、响应时间分布再决定是否需要切换算法。4.2 权重配置与动态调整实操权重是调度算法里一个非常重要的参数。无论你用哪种算法只要能设置权重HAProxy 就会按权重比例影响分发结果。权重的取值范围是 0 到 256默认是 1。权重为 0 表示不向这台后端分发正常流量但健康检查依然会执行这个特性可以用来“软下线”一台机器。我经常用这个方式做无损发布先把 A 机器的权重改成 0等它上面的存量连接处理完再重启服务期间新流量不会进来发布完成后再把权重改回来。通过 socat 或 HAProxy 的 runtime API 动态调整权重的命令如下echo set weight web_servers/web1 0 | socat stdio /var/run/haproxy.sock echo set weight web_servers/web1 2 | socat stdio /var/run/haproxy.sock前提是编译 HAProxy 时带上了 socket 支持并且在全局配置里启用了 stats socketglobal stats socket /var/run/haproxy.sock mode 600 level admin操作的时候注意这种改动是实时生效的但在某些算法下会有短暂的过渡期不会立刻断掉现有连接。4.3 这里顺带聊聊 HAProxy 和 Keepalived 的区别很多人会把 HAProxy 和 Keepalived 放在一起比较甚至搞混它们的分工。我在这里用一个简单的类比解释清楚HAProxy 负责“把流量分给谁”Keepalived 负责“如果 HAProxy 死了谁来接替它”。HAProxy 是应用层的负载均衡器它工作在七层或四层懂得解析 HTTP 协议可以基于 URI、Header、Cookie 做调度。Keepalived 本身不做流量分发它的核心功能是提供 VIP虚拟 IP漂移。当主节点宕机时备用节点自动接管 VIP客户端访问不变但背后的流量处理由备用节点完成。实际生产环境中两者经常配合使用。HAProxy 作为负载均衡器跑在主节点上Keepalived 负责为主节点提供一个高可用的 VIP。当主节点故障时Keepalived 把 VIP 漂移到备用节点备用节点上同样跑着 HAProxy继续接收流量。简单说HAProxy 解决“负载怎么分”的问题Keepalived 解决“机器挂了怎么办”的问题。5. 常见问题与排查技巧实录5.1 会话保持失效都是“哈希源”惹的祸我在排查会话保持失效的问题时最容易踩的坑是哈希源选得不够“稳”。比如用 source 算法做四层会话保持但如果客户端经过了一个大网段的 NAT 网关源 IP 一直在变那同一用户的请求就会分布到不同后端。解决方案有几个。如果是 HTTP 场景优先用 Cookie 做会话保持不要依赖 IP如果必须用 IP可以调整 source 算法的掩码扩大哈希的匹配粒度比如从balance source 255.255.255.255改成balance source 255.255.255.0让同一网段的请求落到同一台后端。但这种方法要慎用网段大的地方会加剧负载不均。还有一种情况是后端的 Cookie 配置不对。HAProxy 计算 hdr(Cookie) 时如果请求里 Cookie 值在用户登录前后发生了变化那前后的请求就会分到不同后端。这时候需要确认 Cookie 中真正稳定的 key 是哪一个必要时用 hdr(Cookie) 搭配高级参数做值提取。5.2 后端负载不均先看算法再看压力负载不均是运维里被问得最多的问题。排查的时候我的顺序一般是固定的。第一步确认健康检查是否正常。如果某台后端一直处于 DOWN 状态所有算法都会自动把它排除剩下的机器承担所有流量自然会显得不均。用show stats检查每台服务器的状态。第二步确认权重设置是否符合预期。很多人改完配置忘了 reload或者权重值写错了导致某一台机器的流量明显偏多。第三步检查算法和后端负载的特征是否匹配。如果是请求耗时差异大的场景但用了 roundrobin不均几乎是必然的这时候要换成 leastconn。第四步确认是否出现哈希倾斜。uri、hdr、url_param、source 这些哈希类算法都存在哈希倾斜的可能性。如果请求的特征值集中在几个值上比如所有用户都访问同一个热搜商品路径那 uri 算法会导致这台后端承受全部压力。这种场景下可以考虑改用 random 或 roundrobin或者在后端前面增加一层缓存。下面是一个排查速查表现象可能原因排查方法一台后端流量明显偏高权重设置过大show stats查看权重一台后端经常被打挂请求耗时差异大但用了轮询切换到 leastconn相同用户请求落到不同后端会话保持方式不可靠改用 Cookie 做会话保持某台后端始终没有流量健康检查失败或权重为 0检查健康检查配置新增后端后大量请求集中到新机哈希算法重新映射评估是否需要一致性哈希5.3 一个我压箱底的调优建议在压测和线上调优时我习惯把 HAProxy 的 stats 页面开启观察每台后端的qcur当前队列长度、scur当前会话数和hrsp_*响应时间百分位这些指标。这些数据能直观地看出算法是否正常工作。开启 stats 页面的配置listen stats bind *:8404 stats enable stats uri /stats在观察时我最关注的是scur的分布。如果使用 leastconn各台后端的scur应该比较接近如果有某台明显高说明那台可能被长连接占住了需要检查它的 maxconn 设置。如果使用 roundrobin各台后端的请求次数应该接近但scur可能差距很大因为请求耗时不等。调优不要一步到位我建议每次只改一个变量。比如先把算法从 roundrobin 改成 leastconn观察一两天确认流量分布和响应时间的变化再决定下一步是调整权重还是调整 maxconn。一次性改太多出了问题很难定位。5.4 HAProxy IngressKubernetes 场景下的调度算法使用最后补充一个与 HAProxy 调度算法相关的热词HAProxy Ingress。这是 HAProxy 在 Kubernetes 场景下作为 Ingress Controller 的实现它的核心职责之一也是调度算法。在 Kubernetes 里Ingress Controller 负责把集群外部流量引入到内部 Service再分发给后端的 Pod。HAProxy Ingress 的配置方式与传统 HAProxy 配置有所不同它通常通过 ConfigMap 或注解来设置负载均衡算法。例如你可以在 Ingress 的注解中指定apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example annotations: haproxy-ingress.github.io/balance-algorithm: leastconn这样一来Pod 的请求分发就会按照 leastconn 算法进行。在 Kubernetes 环境里我建议优先考虑 leastconn 或者 random。原因很简单Pod 的数量经常动态变化实例时多时少单纯用 roundrobin 在 Pod 扩容缩容时会频繁重算而 leastconn 能适配长短不一的请求处理时间random 则对实例变化最不敏感。6. 写在最后的经验做了这么久负载均衡我的体会是调度算法不是越复杂越好也不是越“动态”越高级关键看你后端服务的特征。如果你的后端机器性能均匀、请求处理时间接近roundrobin 就是最简单可靠的选择没必要为了炫技换成别的。如果你的后端请求耗时差异大、机器配置不一致leastconn 才是能真正帮你兜底的算法。哈希类算法虽然能解决会话保持和缓存命中问题但哈希倾斜的风险要时刻记在心里。另外无论选了哪种算法都要把监控配上。调度算法解决的是“分发”层面的事真正能不能扛住流量还要看后端本身的健康程度。HAProxy 的 stats 页面、后端的 CPU/内存/响应时间指标这几样一起看才能把调度算法的效果评估清楚。我踩过的坑不少希望这篇内容能帮你少走一些弯路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询