HAProxy 负载均衡算法全解析:从静态轮询到一致性哈希的实战指南

Haproxy算法实验

静态算法

1.static-rr

static-rr:基于权重的轮询调度

  • 不支持运行时利用socat进行权重的动态调整(只支持0和1,不支持其它值)
  • 不支持端服务器慢启动
  • 其后端主机数量没有限制,相当于LVS中的 wrr

使用场景极少

!NOTE

如果后端服务器从故障恢复或新加入集群,健康检查变为 UP 后。

如果直接给到满权重流量服务器冷启动、缓存为空、连接池没预热,瞬间大量请求涌入,直接被压垮,再次宕机产生雪崩效应

而慢启动是在指定时间内,线性慢慢提升节点有效权重,从零逐步涨到所配置的 weight,流量缓慢爬坡,平滑预热

bash 复制代码
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     static-rr
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1

[root@haproxy ~]# systemctl restart haproxy.service

#测试
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100; done
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10


#检测是否支持热更新
[root@haproxy ~]# echo "get  weight webcluster/haha" | socat  stdio /var/lib/haproxy/stats
2 (initial 2)

[root@haproxy ~]# echo "set  weight  webcluster/haha 1  " | socat stdio /var/lib/haproxy/stats       Backend is using a static LB algorithm and only accepts weights '0%' and '100%'

2.first

first 顺序遍历 server

  • 根据服务器在列表中的位置,自上而下进行调度
  • 其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务
  • 其会忽略服务器的权重设置
  • 不支持用socat进行动态修改权重,可以设置0和1,可以设置其它值但无效

使用场景较少一般用于测试

bash 复制代码
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     first
    server haha 192.168.0.10:80 maxconn 1 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1

[root@haproxy ~]# systemctl restart haproxy.service

#测试:在一个shell中执行持续访问
[Administrator.DESKTOP-VJ307M3] ➤ while true; do curl 172.25.254.100; done
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
.... .....

#在其他设立了中建立持续访问并观察
Administrator.DESKTOP-VJ307M3] ➤  while true; do curl 172.25.254.100; done
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10						#此处出现10信息
webserver2 - 192.168.0.20

动态算法

3.roundrobin

roundrobin 【默认算法,生产最常用】

  • 加权动态轮询,支持 weight 权重
  • 支持运行时动态修改权重 不同于lvs中的wrr轮训模式
  • HAProxy中的roundrobin支持慢启动
  • 其每个后端backend中最多支持4095个real server

适用:HTTP 短连接网站、静态页面

不适合:长连接业务

bash 复制代码
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     roundrobin
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1
    
[root@haproxy ~]# systemctl restart haproxy.service

#测试
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100; done
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10


#动态权重更新

[root@haproxy ~]# echo "get  weight webcluster/haha" | socat  stdio /var/lib/haproxy/stats
2 (initial 2)

[root@haproxy ~]# echo "set  weight  webcluster/haha 1  " | socat stdio /var/lib/haproxy/stats       
[root@haproxy ~]# echo "get  weight webcluster/haha" | socat  stdio /var/lib/haproxy/stats
1 (initial 2)

#效果
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100; done
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10

4.leastconn

leastconn加权的最少连接的动态

  • 特点:智能避开负载高的服务器
  • 支持权重的运行时调整和慢启动,即:根据当前连接最少的后端服务器而非权重进行优先调度(新客户端连接)

适用场景:MySQL等场景

bash 复制代码
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     leastconn
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1
    
[root@haproxy ~]# systemctl restart haproxy.service

[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100; done
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20
webserver1 - 192.168.0.10
webserver2 - 192.168.0.20

混合算法

其它算法即可作为静态算法,又可以通过选项成为动态算法

5.source

source 源地址哈希

根据客户端源 IP做 hash 计算,同一个 IP 永远调度到同一台后端。

适用场景:四层 TCP 代理、无 cookie 的 TCP 业务,简单会话保持;

缺点:多个用户出口同一个公网 IP(公司内网),会挤到同一台服务器;

同时此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器,默认为静态方式,但是可以通过hash-type支持的选项更改这个算法一般是在不插入Cookie的TCP模式下使用,也可给拒绝会话cookie的客户提供最好的会话粘滞性

该算法将客户端请求转发至后端节点时,存在两种服务器选取计算方式:普通取模哈希一致性哈希

普通取模哈希局限性:

普通取模法,公式为哈希值对在线节点总数取模。一旦节点数量发生变化,模基数改变,绝大多数客户端映射关系失效,产生哈希震荡,大量会话断开

复制代码
比如当源hash值时1111,1112,1113,三台服务器a b c的权重均为1,
即abc的调度标签分别会被设定为 0 1 2(1111%3=1,1112%3=2,1113%3=0)
1111 ----- > nodeb
1112 ------> nodec
1113 ------> nodea
如果a下线后,权重数量发生变化
1111%2=1,1112%2=0,1113%2=1
1112和1113被调度到的主机都发生变化,这样会导致会话丢失

一致性哈希核心原理:哈希环(圆环)

  1. 将哈希值空间抽象成一个 0 ~ 2³² 的环形空间
  2. 将每一台后端服务器 IP 做哈希,映射到环上的多个位置(虚拟节点)
  3. 客户端源 IP 算出哈希值,沿着圆环顺时针找到第一个遇到的后端节点

这样节点上下线时,仅一小部分流量发生迁移,最大限度保护已有会话,适合频繁扩缩容的长连接业务

bash 复制代码
#默认静态算法
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     source  #无参数默认使用普通取模哈希
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1
    
[root@haproxy ~]# systemctl restart haproxy.service


#测试:
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100; done
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20


#source动态算法
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     source
    hash-type 	consistent # 切换哈希实现
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1
    
[root@haproxy ~]# systemctl restart haproxy.service


#测试:
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100; done
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20

6.uri(七层 HTTP 专用)

对请求 URL 路径做哈希

  • 基于对用户请求的URI的左半部分或整个uri做hash,再将hash结果对总权重进行取模后
  • 根据最终结果将请求转发到后端指定服务器
  • 默认是静态算法,也可以通过hash-type指定map-based和consistent,来定义使用取模法还是一致性hash

适用场景:缓存服务器集群 CDN服务商,蓝汛、百度、阿里云、腾讯

bash 复制代码
#主备实验环境
[root@webserver1 ~]# echo RS1 - 192.168.0.10 > /var/www/html/index1.html
[root@webserver1 ~]# echo RS1 - 192.168.0.10 > /var/www/html/index2.html
[root@webserver2 ~]# echo RS2 - 192.168.0.20 > /var/www/html/index1.html
[root@webserver2 ~]# echo RS2 - 192.168.0.20 > /var/www/html/index2.html

#设定uri算法
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     uri
    hash-type 	consistent
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1
    
[root@haproxy ~]# systemctl restart haproxy.service

#测试:
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100/index.html; done
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
                                                                                                    ✔
─────────────────────────────────────────────────────────────────────────────────────────────────────
[2026-01-25 14:51.59]  ~
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100/index2.html; done
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
RS2 - 172.168.0.20
                    

7.url_param

抓取 URL GET 参数 进行 hash

  • 同一个 userid 固定访问同一后端

适用场景:根据用户 ID 保持会话 可以实现session保持

bash 复制代码
#主备实验环境
[root@webserver1 ~]# echo RS1 - 192.168.0.10 > /var/www/html/index1.html
[root@webserver1 ~]# echo RS1 - 192.168.0.10 > /var/www/html/index2.html
[root@webserver2 ~]# echo RS2 - 192.168.0.20 > /var/www/html/index1.html
[root@webserver2 ~]# echo RS2 - 192.168.0.20 > /var/www/html/index2.html

#设定url_param算法
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     url_param name
    hash-type 	consistent
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1
    
[root@haproxy ~]# systemctl restart haproxy.service

#测试:
[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100/index.html?name=lee; done
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20
webserver2 - 192.168.0.20

[Administrator.DESKTOP-VJ307M3] ➤ for i in {1..10}; do curl 172.25.254.100/index.html?name=redhat; done
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10
webserver1 - 192.168.0.10

8.hdr

hdr (header) 请求头哈希

  • 读取 HTTP 请求头的值进行哈希

常用:Cookie、token 实现会话粘性 基于客户端请求报文头部做下一步处理

bash 复制代码
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
    bind        *:80
    balance     hdr(User-Agent)
    hash-type 	consistent
    server haha 192.168.0.10:80 check inter 3s fall 3 rise 5 weight 2
    server hehe 192.168.0.20:80 check inter 3s fall 3 rise 5 weight 1
    
[root@haproxy ~]# systemctl restart haproxy.service


#测试:
[Administrator.DESKTOP-VJ307M3] ➤ curl -A "lee" 172.25.254.100
webserver2 - 192.168.0.20
                                                                                                    ✔
─────────────────────────────────────────────────────────────────────────────────────────────────────
[2026-01-25 15:00.53]  ~
[Administrator.DESKTOP-VJ307M3] ➤ curl -A "lee" 172.25.254.100
webserver2 - 192.168.0.20
                                                                                                    ✔
─────────────────────────────────────────────────────────────────────────────────────────────────────
[2026-01-25 15:00.54]  ~
[Administrator.DESKTOP-VJ307M3] ➤ curl -A "timinglee" 172.25.254.100
webserver2 - 192.168.0.20
                                                                                                    ✔
─────────────────────────────────────────────────────────────────────────────────────────────────────
[2026-01-25 15:01.00]  ~
[Administrator.DESKTOP-VJ307M3] ➤ curl -A "timing" 172.25.254.100
webserver1 - 192.168.0.10                                                 
相关推荐
~光~~2 小时前
【嵌入式linux学习_OV8858 bring up】L2_V4L2与Ov8858
linux·数据库·学习
Kina_C2 小时前
HAProxy 负载均衡实战:从安装配置到高级参数详解
linux·运维·负载均衡·haproxy
月光船幽幽2 小时前
四层公理框架驱动AI健康诊断
人工智能·python·科技·算法·安全
Rabitebla2 小时前
C++ STL 之 set 详解:从底层红黑树到实际应用
开发语言·数据结构·c++·算法·leetcode
wunaiqiezixin2 小时前
MIT 6.S081 syscall 实验篇(lab2):System call tracing (moderate)
linux·unix·os·xv6·mit6.s081
liulilittle2 小时前
无锁多生产者多消费者队列的工程实现剖析——算法、内存模型与性能
算法·多线程·并发·无锁·mpmc·lock-free
凉、介2 小时前
ARMv8-A 指令学习
linux·笔记·学习·嵌入式·arm·指令
郝学胜-神的一滴2 小时前
力扣 692:巧用小顶堆高效求解前K个高频单词
java·数据结构·python·程序人生·算法·leetcode·职场和发展
科学实验家3 小时前
贪心算法:分饼干
算法·贪心算法