HAProxy 原理及配置
一、HAProxy原理
-
Haproxy 是一款负载均衡工具,核心原理可以理解为 “请求中转站 + 智能调度员”:
-
客户端的请求先打到 Haproxy 上,它会根据预设规则(比如轮询、权重、源地址哈希等负载均衡算法),结合后端服务器的状态(通过健康检查判断服务器是否存活、是否过载),把请求转发给合适的后端服务器。
-
它支持四层(基于 TCP/IP 端口)和七层(基于 HTTP/HTTPS 等应用层信息)转发,比如七层可以根据 URL、域名等区分请求,实现动静分离(静态资源给 A 服务器,动态接口给 B 服务器)。同时,它会持续监测后端服务器,一旦发现某台服务器故障,就不再把请求发给它,以此实现流量合理分配和服务稳定性。
LVS 和 HAProxy 的区别
-
后端检测:
-
LVS 对后端 RS 没有健康检测功能,RS 故障还是会把流量调度到此主机上(通常会结合 Keepalived 增加故障排查功能)。HAProxy 对后端有健康检测功能,会自动把流量分发到性能好的主机上。
-
-
层面:
-
LVS 只能支持四层。HAProxy 能支持四层和七层,功能更加全面。
-
-
流量处理:
-
LVS 功能比较单一,所以稳定性极高,适用于百万级流量并发场景。HAProxy 功能全面,工作在七层时需要解析应用层协议,功能复杂,则适用于十万级并发场景。
-
二、基本信息
-
主配置文件:/etc/haproxy/haproxy.cfg
-
子配置文件目录:/etc/haproxy/conf.d/
2.1.global 配置参数
主配置文件 /etc/haproxy/haproxy.cfg 文件内容
# 默认配置文件内容
globallog 127.0.0.1 local2 # 定义全局的 syslog 服务器;日志服务器需要开启 UDP 协议,最多可以定义两个chroot /var/lib/haproxy # 锁定运行目录pidfile /var/run/haproxy.pid # 指定 pid 文件路径maxconn 4000 # 每个 haproxy 进程的最大并发连接数user haproxy # user,group,uid,gid 运行 haproxy 的用户身份group haproxy daemon # 以守护进程运行stats socket /var/lib/haproxy/stats # 套接字文件ssl-default-bind-ciphers PROFILE=SYSTEMssl-default-server-ciphers PROFILE=SYSTEM
# 默认配置文件是上述内容,可在 global 里添加下述内容nbproc 2 # 开启的 haproxy worker 进程数,默认进程数是一个nbthread 1 # 指定每个 haproxy 进程开启的线程数,默认为每个进程一个线程(多进程和多线程只能设置一个) cpu-map 1 0 # 绑定 haproxy worker 进程至指定 CPU,将第 1 个 work 进程绑定至 0 号 CPUcpu-map 2 1 # 绑定 haproxy worker 进程至指定 CPU,将第 2 个 work 进程绑定至 1 号 CPUmaxsslconn N # 每个 haproxy 进程 ssl 最大连接数,用于 haproxy 配置了证书的场景下maxconnrate N # 每个进程每秒创建的最大连接数量spread-checks N # 后端 server 状态 check 随机提前或延迟百分比时间,建议 2-5(20%-50%) 之间,默认值 0
2.2.Proxies 配置 defaults
主配置文件 /etc/haproxy/haproxy.cfg 文件内容
defaultsmode http # HAProxy实例使用的连接协议 log global # 指定日志地址和记录日志条目的 syslog/rsyslog日志设备# 此处的 global表示使用 global配置段中设定的log值。 option httplog # 日志记录选项,httplog表示记录与 HTTP会话相关的各种属性值# 包括 HTTP请求、会话状态、连接数、源地址以及连接时间等option dontlognull # dontlognull表示不记录空会话连接日志option http-server-close # 等待客户端完整HTTP请求的时间,此处为等待10s。option forwardfor except 127.0.0.0/8 #透传客户端真实IP至后端web服务器# 在apache配置文件中加入:<br>%{X-Forwarded-For}i # 后在webserer中看日志即可看到地址透传信息option redispatch # 当server Id对应的服务器挂掉后,强制定向到其他健康的服务器,重新派发option http-keep-alive # 开启与客户端的会话保持retries 3 # 连接后端服务器失败次数timeout http-request 10s # 等待客户端请求完全被接收和处理的最长时间timeout queue 1m # 设置删除连接和客户端收到503或服务不可用等提示信息前的等待时间timeout connect 120s # 设置等待服务器连接成功的时间timeout client 600s # 设置允许客户端处于非活动状态,即既不发送数据也不接收数据的时间timeout server 600s # 设置服务器超时时间,即允许服务器处于既不接收也不发送数据的非活动时间 timeout http-keep-alive 60s # session 会话保持超时时间,此时间段内会转发到相同的后端服务器timeout check 10s # 指定后端服务器健康检查的超时时间maxconn 3000 # 最大并发连接数
2.3.Proxies 配置 frontend
主配置文件 /etc/haproxy/haproxy.cfg 文件内容
frontend lee-webserver-80 bind 172.25.254.100:80mode httpuse_backend lee-webserver-80-RS # 调用 backend 的名称
2.4.Proxies 配置 backend
-
定义后端服务器
backend lee-webserver-80-RSmode httpserver web1 192.168.0.101:80 check inter 3s fall 3 rise 5server web2 192.168.0.102:80 check inter 3s fall 3 rise 5
2.5.Proxies 配置 listen
使用 listen 替换 frontend 和 backend 的配置方式
listen webserver_80bind 172.25.254.100:80mode httpoption forwardforserver webserver1 192.168.0.101:80 check inter 3s fall 3 rise 5server webserver2 192.168.0.102:80 check inter 3s fall 3 rise 5
三、热更新 socat
socat 在 HAProxy 热更新中作为指令中介,通过 Unix Domain Socket 与 HAProxy 管理套接字通信,传递热更新指令并促使旧进程暴露监听套接字的文件描述符;新进程继承该描述符以共享监听端口,接管新连接,而旧进程则停止接收新请求、处理完存量连接后优雅退出,最终实现零中断的配置更新或版本升级。
热更新:不停机更新,不停止服务,动态更新策略。
3.1.热更新配置
# 修改配置文件
vim /etc/haproxy/haproxy.cfg
########stats socket /var/lib/haproxy/stats # 在 global 里添加# 作用:开启进程内统计信息 socket 接口的指令,主要作用是提供一个本地通信接口,用于实时查询、监控和管理 HAProxy 运行状态
########
3.2.热更新测试
# 测试热更新
# 设置权重
echo "set weight webcluster/web1 1 " | socat stdio /var/lib/haproxy/stats
echo "set weight webcluster/web1 2 " | socat stdio /var/lib/haproxy/stats
# 下线后端服务器
echo "disable server webcluster/web1 " | socat stdio /var/lib/haproxy/stats
# 上线后端服务器
echo "enable server webcluster/web1 " | socat stdio /var/lib/haproxy/stats
四、HAProxy 算法
4.1.静态算法
4.1.1.static-rr(静态轮询)
-
原理:按预先定义的服务器顺序依次分发请求,不考虑服务器负载或状态变化。
-
特点:
-
服务器权重固定,启动后权重无法动态调整(重启 HAProxy 才能生效)。
-
不支持 “慢启动”(新加入的服务器会立即接收等量请求)。
-
不支持 “socat 热更新”。
-
-
适用场景:后端服务器性能相近、无动态扩缩容需求的场景(如静态资源服务器集群)。
4.1.2. first(优先分配)
-
原理:优先将所有请求分配给第一个可用服务器,直到其达到最大连接数限制,再依次分配给下一个服务器。
-
特点:
-
依赖 maxconn 参数(服务器最大连接数)触发切换。
-
第一个服务器会承担大部分负载,其他服务器仅作为 “备用”。
-
不支持 “socat 热更新”。
-
-
适用场景:需要指定 “主服务器” 处理大部分请求,其他服务器仅在主服务器满载时备用的场景。
4.2.动态算法
4.2.1.roundrobin(动态轮询,默认算法)
-
原理:按权重比例轮流分发请求,支持动态调整权重和 “慢启动”(新服务器逐步增加负载)。
-
特点:
-
权重可动态修改,支持 “socat 热更新”(无需重启 HAProxy)。
-
支持 “慢启动”(slowstart 参数),新加入的服务器会逐渐接收请求,避免瞬间过载。
-
基于服务器当前状态(如健康检查结果)动态排除故障节点。
-
-
适用场景:大多数通用场景,尤其是后端服务器性能存在差异、需要动态调整负载的集群(如 Web 应用服务器)。
4.2.2.leastconn(最小连接数)
-
原理:优先将请求分配给当前活跃连接数最少的服务器。
-
特点:
-
动态考虑服务器负载,避免某台服务器因连接过多而过载。
-
权重会影响分配比例(权重高的服务器可处理更多连接)。
-
权重可动态修改,支持 “socat 热更新”(无需重启 HAProxy)。
-
支持 “慢启动”(slowstart 参数),新加入的服务器会逐渐接收请求,避免瞬间过载。
-
-
适用场景:长连接场景(如数据库连接、WebSocket),或请求处理时间差异较大的服务(如复杂计算任务)。
4.3.哈希类算法
source、uri、uri_param、hdr,其静态 / 动态特性由 hash-type 决定(consistent 为动态,map-based 为静态);其中 source 支持 TCP/HTTP,后三者仅支持 HTTP。
map-base 取模法
HAProxy 中的 bash-type map-based(取模法)是哈希类算法的默认模式,其原理是先对请求的键值(如客户端 IP、URI 等)计算哈希值,再用该值对后端服务器数量取模,得到的结果即为目标服务器索引。这种方式实现简单、计算高效,但缺点是当后端服务器数量变化时,大部分请求的映射关系会失效,导致会话中断或缓存失效,因此仅适合服务器数量固定的场景,不适合动态扩缩容环境。
一致性hash
一致性哈希通过将服务器和请求键值映射到一个首尾相连的哈希环上,实现请求与服务器的稳定绑定:先对服务器 IP 或名称计算哈希值并分布在环上,再对请求键值(如客户端 IP)计算哈希值,顺时针找到环上第一个服务器作为目标;当服务器增减时,仅影响其在环上相邻的少量请求映射,大幅减少因集群变动导致的请求分配震荡,尤其适合动态扩缩容场景。
4.3.1.source(源地址哈希)
-
原理:根据客户端 IP 地址哈希计算,将同一客户端的请求固定分发到同一台后端服务器。
-
特点:
-
实现 “会话保持”(同一客户端多次请求落在同一服务器)。
-
若后端服务器数量变化(如新增 / 移除节点),会导致大部分客户端的哈希结果变化(需配合 hash-type consistent 减少波动)。
-
-
适用场景:需要会话保持的服务(如未使用分布式会话的应用),或希望客户端与服务器建立长期关联的场景。
4.3.2.uri(URI 哈希)
-
原理:根据请求的 URI(统一资源标识符,如 /api/user)哈希计算,将相同 URI 的请求分发到同一服务器。
-
特点:
-
相同路径的请求会被定向到同一服务器,利于缓存(如静态资源服务器的本地缓存复用)。
-
可通过
uri_len
限制哈希计算的 URI 长度(如 uri_len 10 表示仅取前 10 个字符)。
-
-
适用场景:静态资源服务(如图片、JS/CSS)、API 网关(同一接口路径的请求集中处理)。
4.3.3.uri_param(URI 参数哈希)
-
原理:提取 URI 中指定参数的值进行哈希,将参数值相同的请求分发到同一服务器。
-
配置示例:balance uri_param user_id 表示根据 ?user_id=xxx 中的 user_id 值哈希。
-
特点:
-
比 uri 更灵活,可基于业务参数(如用户 ID、订单 ID)定向请求。
-
若参数不存在,会退化为轮询算法。
-
-
适用场景:需要按业务参数定向请求的场景(如按用户 ID 分片的服务)。
4.3.4.hdr(HTTP 头哈希)
-
原理:根据指定的 HTTP 请求头字段值哈希,将头字段相同的请求分发到同一服务器。
-
配置示例:balance hdr(User-Agent) 表示根据 User-Agent 头哈希;balance hdr(Host) 按域名分发。
-
扩展:hdr_balance 可指定多个头字段(如 hdr_balance Host User-Agent),按优先级哈希。
-
适用场景:需要按 HTTP 头信息定向请求的场景(如多域名站点、按客户端类型分流)。
4.4.总结:算法选择建议
-
通用场景首选 roundrobin(动态调整能力强)。
-
长连接 / 负载不均场景用 leastconn。
-
会话保持用 source(客户端 IP 绑定)。
-
缓存优化用 uri 或 uri_param。
-
按 HTTP 头分流用 hdr。
实际配置时,需结合后端服务特性(如会话存储、缓存机制、负载能力)选择最合适的算法。
五、HAProxy 配置
5.1.IP 透传
5.1.1.四层 IP 透传
haproxy
修改配置文件 listen 模块
listen webclusterbind *:80mode tcp balance roundrobinoption forwardforhash-type consistentcookie WEBCOOKIE insert nocache indirectserver web1 192.168.13.10:80 send-proxy cookie servera check inter 5s fall 3server web2 192.168.13.20:80 send-proxy cookie serverb check inter 5s fall 3
RS
http {log_format main '$remote_addr - $remote_user [$time_local] "$request" ''"$proxy_protocol_addr"''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';...
include /etc/nginx/conf.d/*.conf;
server {listen 80 proxy_protocol;listen [::]:80;server_name _;root /usr/share/nginx/html;
验证
[root@rs2 ~]# cat /var/log/nginx/access.log
192.168.13.100 - - [20/Jul/2025:09:55:58 +0800] "GET / HTTP/1.1" "192.168.67.123"200 14 "-" "curl/7.76.1" "-"
192.168.13.100 - - [20/Jul/2025:09:55:59 +0800] "GET / HTTP/1.1" "192.168.67.123"200 14 "-" "curl/7.76.1" "-"
192.168.13.100 - - [20/Jul/2025:09:55:59 +0800] "GET / HTTP/1.1" "192.168.67.123"200 14 "-" "curl/7.76.1" "-"
192.168.13.100 - - [20/Jul/2025:09:56:00 +0800] "GET / HTTP/1.1" "192.168.67.123"200 14 "-" "curl/7.76.1" "-
5.2.HAProxy 错误页面
5.2.1.自定义错误文件
haproxy 配置自定义错误文件
rpm -qa | grep haproxy # 查看软件包
rpm -ql haproxy-2.4.22-1.el9.x86_64 | grep http # 查看默认错误页面文件位置
...
/usr/share/haproxy/400.http
/usr/share/haproxy/403.http
/usr/share/haproxy/408.http
/usr/share/haproxy/500.http
/usr/share/haproxy/502.http
/usr/share/haproxy/503.http
/usr/share/haproxy/504.http
mkdir -p /haproxy/errorpages # 创建自定义错误文件存放目录
cp /usr/share/haproxy/503.http /haproxy/errorpages/503page.http
cat /haproxy/errorpages/503page.http << EOF
HTTP/1.0 503 Service Unavailable^M
Cache-Control: no-cache^M
Connection: close^M
Content-Type: text/html;charset=UTF-8^M
^M
<html><body><h1>哈哈</h1>
嘻嘻
</body></html>
EOF
vim /etc/haproxy/haproxy.cfg
defaults
...errorfile 503 /haproxy/errorpages/503page.http # 添加此行内容
...
systemctl restart haproxy.service
测试
后端 RS 停止 nginx 服务
systemctl stop nginx.service
浏览器访问 haproxy
5.2.2.错误页面重定向
haproxy 配置错误页面重定向
vim /etc/haproxy/haproxy.cfg
defaults
...errorloc 503 https://www.baidu.com # 添加此行内容
...
systemctl restart haproxy.service
测试
后端 RS 停止 nginx 服务
systemctl stop nginx.service
浏览器访问 haproxy,将重定向到 baidu
5.3.HAProxy 四层负载
mysql 四层负载实验
后端 RS 主机配置
# 安装 mariadb
yum install mariadb-server -y
vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
...
server-id=10 # 添加此行,注意两个主机的 id 需要不一致
...
systemctl enable --now mariadb
mysql -e "grant all on *.* to zyz@'%' identified by 'zyz'" # 添加可远程登录的用户
# 测试:远程登录 mysql
mysql -uzyz -pzyz -h192.168.13.10
mysql -uzyz -pzyz -h192.168.13.20
测试 mysql server_id
haproxy 主机配置
vim /etc/haproxy/haproxy.cfg
...
listen sqlclusterbind *:3306mode tcp balance roundrobin # 为了测试用 roundrobin 模式,真实环境会用 source 源地址 hash 模式server sql1 192.168.13.10:3306 checkserver sql2 192.168.13.20:3306 check
...
systemctl restart haproxy.service
# 测试
netstat -antlupe | grep 3306
client 测试
用 haproxy 主机登录后端 rs 数据库
mysql -uzyz -pzyz -h192.168.67.100
mysql -uzyz -pzyz -h192.168.67.100
7.9.HAProxy https 实现
haproxy 主机配置
在 haproxy 主机上生成证书
mkdir /etc/haproxy/certs/
openssl req -newkey rsa:2048 -nodes -sha256 -keyout /etc/haproxy/certs/haha.org.key -x509 -days 365 -out /etc/haproxy/certs/haha.org.crt
cd /etc/haproxy/certs/
cat haha.org.crt haha.org.key > haha.pem # 指定 crt 后证书文件为 pem 格式,需要同时包含证书和所有私钥
配置文件
vim /etc/haproxy/haproxy.cfg
##########
frontend webcluster-80bind *:80mode httpredirect scheme https if !{ ssl_fc } # http 非加密流量会自动转为 https 加密传输balance roundrobinuse_backend webserver
frontend webcluster-443bind *:443 ssl crt /etc/haproxy/certs/haha.pemmode httpbalance roundrobinuse_backend webserver
backend webserverserver web1 192.168.13.10:80 check inter 5 fall 3 rise 2server web2 192.168.13.20:80 check inter 5 fall 3 rise 2
##########
systemctl restart haproxy.service
测试
浏览器访问 http://192.168.67.100 会自动变成 https://192.168.67.100
客户端访问 https 轮询效果