负载均衡与集群部署在PHP并发中的最佳实践
一、引言
在现代互联网应用中,单台服务器往往难以承担高并发请求。为了保证系统的可用性、稳定性与可扩展性,需要引入 负载均衡(Load Balancing) 与 集群部署(Clustering)。通过将流量分发到多台后端 PHP 实例,并结合水平扩展(Horizontal Scaling),能够有效提升吞吐能力、降低单点故障风险。
本篇教程将系统地介绍:
- 负载均衡原理与常见方案
- PHP 应用集群部署要点
- 会话管理与共享存储设计
- 实例:Nginx + PHP-FPM 多节点负载均衡
- 进阶:使用 HAProxy、高可用配置与心跳检查
- 容器化与 Kubernetes 部署示例
- 监控与自动伸缩
每个部分都配备 代码示例 与 ASCII 图解,帮助你快速理解并上手实战。
二、负载均衡原理与常见方案
2.1 负载均衡概念
负载均衡的核心在于:将客户端请求分发到多台后端服务器,使得每台服务器承担一部分流量,避免某台机器过载或宕机带来的服务不可用。一个典型的负载均衡架构如下所示:
┌───────────┐
│ │
Client →──│ 负载均衡器 ├─┬→ PHP-FPM Node A
│ │ │
└───────────┘ │
│
├→ PHP-FPM Node B
│
└→ PHP-FPM Node C
在这个架构中,客户端只需访问 1 个公网 IP(负载均衡器),该设备/服务会根据配置将请求分发到后端若干 PHP-FPM 节点。
2.2 常见负载均衡方案
DNS 轮询(Round Robin DNS)
- 将同一个域名解析到多个 A 记录,每个记录指向不同的服务器 IP。
- 优点:简单易用,无需额外设备。
- 缺点:DNS 缓存无法感知节点健康状况,客户端可能在短时间内持续访问已宕机节点。
硬件负载均衡器(F5、Citrix NetScaler 等)
- 专业设备,性能极高,支持 L4/L7 层负载均衡、健康检查、SSL 卸载等功能。
- 优点:稳定、可扩展性强。
- 缺点:成本较高,配置复杂。
软件负载均衡器(Nginx、HAProxy、LVS)
- 通过开源软件在通用服务器上实现负载均衡,常见于中小型及超大规模分布式系统。
- 优点:成本低、配置灵活,可做七层(HTTP)或四层(TCP)路由。
- 缺点:需要自己维护高可用(双机热备、Keepalived 等)。
本教程重点聚焦 Nginx + PHP-FPM 与 HAProxy 两种软件负载均衡方式,并兼顾 LVS + Keepalived 方案。
三、PHP 应用集群部署要点
负载均衡之后,还需关注后端 PHP 应用的集群部署要点,主要包括以下几个方面:
无状态化设计
- 每个请求应尽可能“无状态”:业务数据(用户会话、缓存等)不存储在单台机器本地。
- 常见做法:将会话存储在 Redis/Memcached/数据库;配置文件与静态资源通过共享存储(NFS、OSS)或制品化部署。
会话管理
- 浏览器的 Cookie + PHP Session 机制需要将会话数据保存在集中式存储,否则不同后端节点无法读取。
典型方案:
- Redis Session:在
php.ini
中配置session.save_handler = redis
,将 Session 写入 Redis。 - 数据库 Session:自建一个
sessions
表存储 Session 数据。 - Sticky Session(会话保持):在负载均衡器层面启用“粘性会话”(通过 Cookie 或源 IP 保证某用户请求始终到同一台后端)。
- Redis Session:在
共享存储与制品化部署
- 应用代码、静态资源(图片、CSS、JS)应通过制品化方式(如将构建好的代码打包上传到各节点或使用镜像),避免单点共享文件系统。
若确需共享文件(如上传文件),可使用:
- NFS:性能受限,带宽瓶颈需评估。
- 对象存储(OSS/S3):将上传文件直接发到对象存储,通过 CDN 分发静态资源。
日志与监控
- 日志集中化:使用 ELK、Fluentd、Prometheus 等,将各节点日志聚合,方便排查与监控。
- 健康检查:负载均衡器需要对后端节点定期做健康检查(HTTP /health 检测接口),将不健康节点自动剔除。
水平扩展与自动伸缩
- 当流量激增时,动态扩容新的 PHP-FPM 节点;业务低峰时再缩容。
- 可结合 Docker + Kubernetes 实现自动伸缩(Horizontal Pod Autoscaler)并与负载均衡器联动。
四、示例一:Nginx + PHP-FPM 多节点负载均衡
下面以 Nginx 为负载均衡器,后端有三台 PHP-FPM 节点举例,展示完整配置与部署思路。
4.1 目录与服务概览
- 负载均衡服务器(LB):IP 假设为
10.0.0.1
,运行 Nginx 作为 HTTP L7 负载均衡。 - PHP-FPM 节点:三台服务器,IP 分别为
10.0.0.11
、10.0.0.12
、10.0.0.13
,均部署相同版本的 PHP-FPM 与应用代码。
节点拓扑示意:
┌─────────┐
Client ──────>│ 10.0.0.1│ (Nginx LB)
└─────────┘
│ │ │
┌────────────┴ │ ┴─────────────┐
│ │ │
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PHP-FPM Node │ │ PHP-FPM Node │ │ PHP-FPM Node │
│ 10.0.0.11 │ │ 10.0.0.12 │ │ 10.0.0.13 │
└──────────────┘ └──────────────┘ └──────────────┘
4.2 Nginx 负载均衡配置示例
将以下配置存放在 Nginx 主配置目录 /etc/nginx/conf.d/lb.conf
:
# /etc/nginx/conf.d/lb.conf
upstream php_backend {
# 三台后端 PHP-FPM 节点,使用 IP:端口 形式
# 端口假设为 9000,即 PHP-FPM 监听 127.0.0.1:9000
server 10.0.0.11:9000 weight=1 max_fails=3 fail_timeout=30s;
server 10.0.0.12:9000 weight=1 max_fails=3 fail_timeout=30s;
server 10.0.0.13:9000 weight=1 max_fails=3 fail_timeout=30s;
# 可选:使用 least_conn(最少连接数)策略
# least_conn;
}
server {
listen 80;
server_name www.example.com;
root /var/www/html/myapp/public;
index index.php index.html;
# 健康检查接口
location /health {
return 200 'OK';
}
# 所有 PHP 请求转发到负载均衡后端
location ~ \.php$ {
# FastCGI 参数
include fastcgi_params;
fastcgi_index index.php;
# 转发到 upstream
fastcgi_pass php_backend;
# 脚本文件路径,根据实际情况调整
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# 静态资源可由 LB 直接处理,降低后端压力
location ~* \.(?:css|js|gif|jpe?g|png|svg)$ {
expires 30d;
add_header Cache-Control "public";
}
}
说明与要点
upstream php_backend { ... }
:定义后端 PHP-FPM 节点池。weight=1
:权重值,可根据节点性能分配(例如更强节点权重可调高)。max_fails=3 fail_timeout=30s
:如果某节点在 30 秒内失败超过 3 次,会被暂时标记为不可用。- 默认的负载均衡策略为 轮询(Round Robin),可用
least_conn;
切换为“最少连接数”策略。
location ~ \.php$ { fastcgi_pass php_backend; }
:将所有 PHP 请求转发给php_backend
中定义的 PHP-FPM 节点池。健康检查:
- 简化实现:使用
/health
路径返回 200,NGINX 自身不具备主动健康检查,但可与第三方模块(如nginx_upstream_check_module
)结合。 - 若希望更完善,需要使用 HAProxy 或者利用 Keepalived + LVS 做二级心跳检测。
- 简化实现:使用
- 静态资源直出:对
.css
、.js
、.jpg
等静态文件直接响应,避免转发给 PHP 后端,降低后端压力。
部署步骤概览:
# 在负载均衡器(10.0.0.1)上安装 Nginx
sudo yum install nginx -y # 或 apt-get install nginx
sudo systemctl enable nginx
sudo systemctl start nginx
# 将 lb.conf 放到 /etc/nginx/conf.d/
scp lb.conf root@10.0.0.1:/etc/nginx/conf.d/
# 检查配置、重启 Nginx
nginx -t && systemctl reload nginx
4.3 PHP-FPM 节点配置
在每台后端服务器(10.0.0.11/12/13)上,部署相同版本的 PHP-FPM 应用:
PHP-FPM 配置(常见路径
/etc/php-fpm.d/www.conf
):[www] user = www-data group = www-data listen = 0.0.0.0:9000 ; 监听 0.0.0.0:9000 端口,便于 Nginx 远程连接 pm = dynamic pm.max_children = 50 ; 根据服务器内存与负载调整 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 10 pm.max_requests = 500
应用代码部署
- 将最新的 PHP 应用代码部署到
/var/www/html/myapp/
下,确保public/index.php
等入口文件存在。 - 禁止在本地保存上传文件:改为使用 对象存储(OSS、S3) 或 NFS 挂载。
- 将最新的 PHP 应用代码部署到
Session 存储配置
推荐使用 Redis,修改
php.ini
:session.save_handler = redis session.save_path = "tcp://redis-master:6379"
- 若使用文件存储,则需将
session.save_path
指向共享存储,如 NFS 挂载路径:session.save_path = "/mnt/shared/sessions"
。
启动 services:
sudo yum install php-fpm php-mbstring php-redis php-fpm -y # 或对应包管理器 systemctl enable php-fpm systemctl start php-fpm
完成以上配置后,Nginx LB 将会把所有 PHP 请求分发到 10.0.0.11/12/13 三台节点,形成一个基本的 Nginx + PHP-FPM 集群。
五、示例二:HAProxy 负载均衡 & 高可用配置
如果需要更灵活的 L4/L7 负载均衡能力(如更细粒度的健康检查、TCP 代理、SSL 卸载),可以考虑使用 HAProxy。以下示例演示如何用 HAProxy 做 PHP-FPM 节点池,并结合 Keepalived 实现高可用 VIP。
5.1 HAProxy 配置示例
在负载均衡器服务器(10.0.0.1)上安装并配置 HAProxy:
sudo yum install haproxy -y # 或 apt-get install haproxy
在 /etc/haproxy/haproxy.cfg
中添加:
global
log 127.0.0.1 local0
maxconn 20480
daemon
defaults
log global
mode http
option httplog
option dontlognull
retries 3
timeout connect 5s
timeout client 30s
timeout server 30s
frontend http_frontend
bind *:80
default_backend php_backend
backend php_backend
balance roundrobin
option httpchk GET /health
server web1 10.0.0.11:9000 check
server web2 10.0.0.12:9000 check
server web3 10.0.0.13:9000 check
要点说明
frontend http_frontend
:监听 80 端口,所有 HTTP 流量导入本前端;通过default_backend
转发到后端节点池。backend php_backend
:三台 PHP-FPM 节点,使用balance roundrobin
做轮询;option httpchk GET /health
:HAProxy 会定期对每个节点发起 GET/health
请求(如前文 Nginx 配置的健康检查接口),若返回非 200,则剔除该节点。check
:启动健康检查。
- HAProxy 本身可做 SSL 终端 (
bind *:443 ssl crt /path/to/cert.pem
),并通过backend php_backend
将解密后的流量转发给后端。
5.2 Keepalived 高可用示例
为了避免单台负载均衡器故障,需要在两台或更多 HAProxy 服务器上部署 Keepalived,通过 VRRP 协议保证 VIP(Virtual IP)漂移到可用节点。
在两台 LB 服务器上(假设 IP 为 10.0.0.1 与 10.0.0.2)安装 keepalived
:
sudo yum install keepalived -y # 或 apt-get install keepalived
在第一台 10.0.0.1
上 /etc/keepalived/keepalived.conf
:
vrrp_instance VI_1 {
state MASTER
interface eth0 # 根据实际网卡名调整
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass SecretPass
}
virtual_ipaddress {
10.0.0.100/24 # 虚拟 IP,切换到 MASTER
}
}
在第二台 10.0.0.2
上 /etc/keepalived/keepalived.conf
:
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90 # 次级备份
advert_int 1
authentication {
auth_type PASS
auth_pass SecretPass
}
virtual_ipaddress {
10.0.0.100/24
}
}
工作方式
- VRRP 协议:MASTER 节点(优先级更高)持有虚拟 IP(10.0.0.100)。若 MASTER 宕机或网络不通,BACKUP 会接管 VIP,实现无缝切换。
- HAProxy:在两台机器上均运行 HAProxy,接收 VIP 上的流量。
- 客户端:只需要访问
10.0.0.100:80
,背后由 Keepalived 动态绑定到可用的 LB 节点上。
六、示例三:容器化与 Kubernetes 集群部署
为了进一步提升扩展与运维效率,越来越多的团队将 PHP 应用容器化,并在 Kubernetes 上部署。以下示例展示如何在 k8s 中部署一个 PHP-FPM 后端服务,并使用 Service + Ingress 做负载均衡。
6.1 前提:准备 Docker 镜像
假设已经有一个基于 PHP-FPM + Nginx 的 Docker 镜像,包含应用代码。以下为示例 Dockerfile
简化版:
# Dockerfile
FROM php:7.4-fpm-alpine
# 安装必要扩展
RUN docker-php-ext-install pdo pdo_mysql
# 复制应用代码
COPY . /var/www/html
# 安装 Nginx
RUN apk add --no-cache nginx supervisor \
&& mkdir -p /run/nginx
# Nginx 配置
COPY docker/nginx.conf /etc/nginx/nginx.conf
# Supervisor 配置
COPY docker/supervisord.conf /etc/supervisord.conf
EXPOSE 80
CMD ["supervisord", "-c", "/etc/supervisord.conf"]
示例 nginx.conf
:(仅演示关键部分)
worker_processes auto;
events { worker_connections 1024; }
http {
include mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000; # PHP-FPM
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
}
构建并推送镜像到私有/公有镜像仓库:
docker build -t myregistry.com/myapp/php-fpm:1.0 .
docker push myregistry.com/myapp/php-fpm:1.0
6.2 Kubernetes Deployment 与 Service
在 k8s 中创建 php-fpm
Deployment 与对应的 ClusterIP
Service:
# k8s/php-fpm-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-fpm
labels:
app: php-fpm
spec:
replicas: 3 # 三个副本,水平扩展
selector:
matchLabels:
app: php-fpm
template:
metadata:
labels:
app: php-fpm
spec:
containers:
- name: php-fpm
image: myregistry.com/myapp/php-fpm:1.0
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
# k8s/php-fpm-service.yaml
apiVersion: v1
kind: Service
metadata:
name: php-fpm-svc
spec:
selector:
app: php-fpm
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
应用上述配置:
kubectl apply -f k8s/php-fpm-deployment.yaml
kubectl apply -f k8s/php-fpm-service.yaml
6.3 Ingress Controller 负载均衡
在 Kubernetes 集群中,通常使用 Ingress 来对外暴露 HTTP 服务。以 Nginx Ingress Controller 为例,创建一个 Ingress 资源,将流量导向 php-fpm-svc
:
# k8s/php-fpm-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: php-fpm-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: www.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: php-fpm-svc
port:
number: 80
应用 Ingress:
kubectl apply -f k8s/php-fpm-ingress.yaml
此时,外部通过访问 www.example.com
(需要 DNS 解析到 Ingress Controller 的 LB IP)即可访问后端 PHP-FPM 服务,k8s 会自动将请求分发到三台 Pod。
ASCII 拓扑图
[Client 请求 www.example.com]
│
▼
┌────────────────────────┐
│ Kubernetes Ingress LB │ <- NodePort/LoadBalancer
└────────────────────────┘
│
▼
┌────────────────────────┐
│ ClusterIP 服务:80 │ (php-fpm-svc)
└────────────────────────┘
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Pod A │ │ Pod B │ │ Pod C │ (php-fpm Deployment, replicas=3)
└────────┘ └────────┘ └────────┘
6.4 自动伸缩与弹性扩容
通过 Kubernetes 的 Horizontal Pod Autoscaler(HPA),可以根据 CPU/内存或自定义指标自动伸缩 Pod 数量。示例:当 CPU 利用率超过 60% 时,将 Pod 数自动扩展到最大 10 个。
# k8s/php-fpm-hpa.yaml
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: php-fpm-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-fpm
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
应用 HPA:
kubectl apply -f k8s/php-fpm-hpa.yaml
这样,当当前三台 Pod 的平均 CPU 使用率 > 60% 时,Kubernetes 会自动增加 Pod 数量;当 CPU 低于 60% 时,会自动缩容。
七、监控与高可用运维
7.1 健康检查与故障隔离
- HTTP 健康检查:在 Nginx/HAProxy 中配置
/health
路径要求返回200 OK
。 PHP-FPM 内部健康:在应用中实现简单的健康检查接口:
<?php // public/health.php header('Content-Type: text/plain'); // 可检查数据库、Redis 等依赖是否可用 echo "OK";
- Kubernetes Liveness/Readiness Probe:见前文 Deployment 中的配置,通过
livenessProbe
与readinessProbe
指定/health
路径。
7.2 日志与指标收集
访问日志与错误日志:
- 负载均衡器(Nginx/HAProxy)记录请求日志。
- 后端 PHP-FPM 节点记录 PHP 错误日志与业务日志。
- 可使用 Filebeat/Fluentd 将日志采集到 ElasticSearch 或 Loki。
应用指标监控:
- 利用 Prometheus + Node Exporter 监控系统资源(CPU、内存、负载)。
- 利用 PHP-FPM Exporter 等收集 FPM 进程数、慢请求数等指标。
- 结合 Grafana 做可视化告警。
- 链路追踪:在业务中集成 OpenTelemetry 或 SkyWalking,实现请求链路追踪,方便性能瓶颈定位。
7.3 灰度发布与滚动更新
在集群环境下,为了做到零停机更新,可以采用以下策略:
Nginx/HAProxy 权重平滑切换:
- 先在负载均衡器上调整新旧版本权重,将流量逐渐导向新版本,待稳定后下线旧版本。
Kubernetes 滚动更新:
- 将 Deployment 的
spec.strategy.type
设置为RollingUpdate
(默认),并配置maxSurge: 1
、maxUnavailable: 0
,保证每次只更新一个 Pod。 - 结合
readinessProbe
,保证新 Pod 完全就绪前不会接收流量。
- 将 Deployment 的
蓝绿部署/灰度发布:
- 通过创建 两套环境(Blue/Green),切换 Ingress 或 Service 的流量指向,完成单次切换式发布。
八、常见问题与 FAQ
Q:为什么访问某些请求会一直卡住?
- A:可能后端 PHP-FPM 进程已满(
pm.max_children
配置过小),导致请求排队等待。应及时监控php-fpm
进程使用情况,并根据流量调整pm.*
参数。
- A:可能后端 PHP-FPM 进程已满(
Q:如何处理用户 Session?
- A:务必使用集中式存储(Redis、Memcached、数据库)保存 Session,禁止写在本地文件;否则当请求被分发到不同节点时会出现“登录态丢失”。
- 同时可开启 “粘性会话”(Sticky Session),但更推荐使用集中式存储以便水平扩展。
Q:负载均衡为何会频繁剔除后端节点?
- A:检查后端节点的健康检查接口(如
/health
)是否正常返回 200;若应用启动较慢,请将initialDelaySeconds
设大一些,避免刚启动时被判定为不健康。
- A:检查后端节点的健康检查接口(如
Q:NFS 共享存储性能太差,有何替代方案?
- A:推荐直接将上传文件发到对象存储(如 AWS S3、阿里OSS),并通过 CDN 分发静态资源;如果必须用单机文件,需要评估带宽并配合缓存加速。
Q:Kubernetes Ingress 性能比 Nginx/HAProxy 差?
- A:K8s Ingress Controller 经常是基于 Nginx 或 Traefik。如果流量巨高,可考虑使用 MetalLB + BGP 或Cloud LoadBalancer,让 Ingress Controller 只做七层路由,四层负载交给 Cloud Provider。
Q:负载均衡器过载或成单点?
- A:若只部署一台 Nginx/HAProxy,LB 本身会成为瓶颈或单点。可通过 双机 Keepalived 或云服务 L4/L7 高可用 方案,让 LB 具有高可用能力。
九、总结
本文系统地介绍了在高并发场景下,如何通过 负载均衡 与 集群部署 实现 PHP 应用的高可用与高吞吐:
- 负载均衡方案对比:包括 DNS 轮询、Nginx、HAProxy、LVS/Keepalived 多种方式。
- Nginx + PHP-FPM 节点池示例:展示了详细的 Nginx upstream 配置与 PHP-FPM 参数调整。
- HAProxy + Keepalived 高可用:演示了基于 TCP/HTTP 健康检查的后端剔除与 VRRP VIP 切换。
- Kubernetes 部署示例:包括 Deployment、Service、Ingress、HPA 自动伸缩配置。
- 并发控制:结合 Selenium、Swoole 协程、ReactPHP 等异步模型,实现了请求并发与速率限制。
- 常见问题与运维建议:覆盖会话管理、共享存储、日志监控、零停机发布等关键环节。
评论已关闭