一、负载均衡基础认知
1.1 为什么需要负载均衡
Node.js是单线程模型,无法充分利用多核CPU。负载均衡可以:
| 收益类型 | 具体内容 | 说明 |
|---|---|---|
| 性能提升 | 充分利用多核CPU | Node.js单进程只占用一个核 |
| 高可用性 | 故障自动转移 | 单点故障不影响服务 |
| 扩展性 | 水平扩展能力 | 增加服务器即可扩展 |
| 维护性 | 零停机部署 | 滚动重启不影响服务 |
1.2 负载均衡架构
## 典型负载均衡架构
客户端请求
↓
负载均衡器(Nginx/HAProxy)
↓
Node.js应用集群(多进程/多机器)
↓
数据库/缓存/外部API
1.3 Node.js负载均衡方案对比
| 方案 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Nginx反向代理 | 七层负载 | 功能丰富、SSL终止 | 配置复杂 | 生产环境推荐 |
| PM2集群 | 进程管理+集群 | 简单、内置 | 单机器限制 | 单机多核 |
| Docker+Swarm | 容器编排 | 容器化部署 | 学习曲线陡 | 微服务架构 |
| HAProxy | 四层/七层负载 | 性能极高 | 功能较少 | 高性能场景 |
二、PM2集群模式配置
2.1 PM2安装与基本概念
# 安装PM2
sudo npm install -g pm2
# 启动应用(单进程)
pm2 start app.js --name "myapp"
# 查看状态
pm2 status
# 查看日志
pm2 logs myapp
# 重启应用
pm2 restart myapp
# 停止应用
pm2 stop myapp
# 删除应用
pm2 delete myapp
2.2 集群模式(推荐)
# 方法一:直接启动集群模式
pm2 start app.js -i max --name "myapp"
# -i max:根据CPU核心数启动对应数量的进程
# 方法二:指定进程数
pm2 start app.js -i 4 --name "myapp"
# 启动4个工作进程
# 方法三:使用配置文件
// ecosystem.config.js
module.exports = {
apps: [{
name: "myapp",
script: "app.js",
instances: "max", // 根据CPU核心数
exec_mode: "cluster", // 集群模式
watch: false,
autorestart: true,
max_memory_restart: "500M",
env: {
NODE_ENV: "development"
},
env_production: {
NODE_ENV: "production"
}
}]
}
# 使用配置文件启动
pm2 start ecosystem.config.js --env production
# 查看集群状态
pm2 status
# 调整进程数(扩容/缩容)
pm2 scale myapp +2 # 增加2个进程
pm2 scale myapp 8 # 调整为8个进程
pm2 scale myapp -2 # 减少2个进程
2.3 PM2集群优化
// app.js - 确保应用支持集群模式
const http = require('http');
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
// 方法一:手动使用cluster模块
if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 正在运行`);
// 衍生工作进程
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 已退出`);
// 自动重启
cluster.fork();
});
} else {
// 工作进程共享同一个TCP连接
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World\n');
}).listen(8000);
console.log(`工作进程 ${process.pid} 已启动`);
}
# 方法二:使用PM2管理(更推荐)
pm2 start app.js -i max --name "myapp"
# 监控集群
pm2 monit
# 查看详细状态
pm2 show myapp
2.4 PM2开机启动
# 生成开机启动脚本
pm2 startup
# 保存当前进程列表
pm2 save
# 恢复进程列表
pm2 resurrect
# 查看启动配置
pm2 list
三、Nginx负载均衡配置
3.1 安装Nginx
# CentOS 7
sudo yum install -y epel-release
sudo yum install -y nginx
# CentOS 8/Stream
sudo dnf install -y nginx
# 启动Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
# 验证
curl http://127.0.0.1
3.2 配置Upstream负载均衡
# /etc/nginx/conf.d/nodejs-upstream.conf
# 定义上游服务器集群
upstream nodejs_cluster {
# 负载均衡算法
# 1. 轮询(默认)
# 2. 加权轮询
# 3. IP哈希(会话保持)
# 4. 最少连接
# 轮询(默认)
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
# 加权轮询
# server 127.0.0.1:3000 weight=3;
# server 127.0.0.1:3001 weight=1;
# IP哈希(会话保持)
# ip_hash;
# 最少连接
# least_conn;
# 健康检查(需要nginx-plus或第三方模块)
# max_fails=3 fail_timeout=30s;
}
# 虚拟主机配置
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://nodejs_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
3.3 启动多个Node.js实例
# 使用PM2启动多个端口实例
pm2 start app.js --name "app-3000" -i 1 -- -p 3000
pm2 start app.js --name "app-3001" -i 1 -- -p 3001
pm2 start app.js --name "app-3002" -i 1 -- -p 3002
pm2 start app.js --name "app-3003" -i 1 -- -p 3003
// app.js - 支持从命令行参数读取端口
const http = require('http');
const port = process.env.PORT || process.argv[2] || 3000;
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from port ${port}\n`);
}).listen(port, () => {
console.log(`Server running on port ${port}`);
});
3.4 Nginx负载均衡算法详解
# 1. 轮询(Round Robin)- 默认
upstream backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
# 每个请求按时间顺序逐一分配到不同后端服务器
# 2. 加权轮询(Weighted Round Robin)
upstream backend {
server 127.0.0.1:3000 weight=3;
server 127.0.0.1:3001 weight=1;
}
# 权重越高,被分配到的概率越大
# 3. IP哈希(IP Hash)- 会话保持
upstream backend {
ip_hash;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
# 同一IP客户端总是访问同一后端服务器
# 4. 最少连接(Least Connections)
upstream backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
# 请求分配到当前活动连接数最少的服务器
# 5. 通用哈希(Generic Hash)
upstream backend {
hash $request_uri consistent;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
# 根据指定键(如URI)分配,支持一致性哈希
3.5 健康检查配置
# 主动健康检查(需要nginx-plus或开源版本打补丁)
upstream backend {
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
}
server {
location / {
proxy_pass http://backend;
# 健康检查失败重试
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 30s;
}
# 健康检查端点
location /health {
access_log off;
return 200 "OK";
}
}
四、多机负载均衡配置
4.1 架构设计
## 多机负载均衡架构
Internet
↓
DNS负载均衡(可选)
↓
Nginx负载均衡器(主)
↓
-------------------------
↓ ↓ ↓
Node.js Node.js Node.js
服务器A 服务器B 服务器C
↓ ↓ ↓
-------------------------
↓
共享数据库/Redis
4.2 Nginx配置多后端
# /etc/nginx/conf.d/nodejs-multi.conf
upstream nodejs_cluster {
# 多机后端
server 192.168.1.101:3000 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.102:3000 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.103:3000 weight=1 max_fails=3 fail_timeout=30s;
# 备用服务器
server 192.168.1.104:3000 backup;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://nodejs_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 跨机器负载均衡优化
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
4.3 多机部署脚本
#!/bin/bash
# Node.js多机部署脚本
# 定义服务器列表
SERVERS=("192.168.1.101" "192.168.1.102" "192.168.1.103")
APP_DIR="/opt/myapp"
APP_PORT=3000
for SERVER in "${SERVERS[@]}"; do
echo "部署到服务器: $SERVER"
# 1. 同步代码
rsync -avz --delete ./ $SERVER:$APP_DIR/
# 2. 安装依赖
ssh $SERVER "cd $APP_DIR && npm install --production"
# 3. 重启应用
ssh $SERVER "pm2 restart myapp || pm2 start $APP_DIR/app.js --name myapp -i max"
# 4. 验证
ssh $SERVER "pm2 status"
done
echo "===== 部署完成 ====="
4.4 高可用Nginx配置
# 主Nginx:/etc/nginx/nginx.conf
upstream nodejs_backend {
ip_hash; # 会话保持
server 192.168.1.101:3000;
server 192.168.1.102:3000;
server 192.168.1.103:3000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://nodejs_backend;
# ... 其他代理配置
}
}
# 配置Keepalived实现Nginx高可用
# 安装Keepalived
sudo yum install -y keepalived
# 主节点配置:/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 1111
}
virtual_ipaddress {
192.168.1.100/24 # 虚拟IP(VIP)
}
}
# 备节点配置
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 50 # 优先级低于主节点
# ... 其他配置相同
}
五、Docker容器化负载均衡
5.1 Docker Compose配置
# docker-compose.yml
version: '3.8'
services:
# Node.js应用(多副本)
app:
build: .
deploy:
replicas: 4 # 启动4个副本
environment:
- NODE_ENV=production
networks:
- app-network
# Nginx负载均衡器
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
networks:
- app-network
networks:
app-network:
driver: overlay # 用于跨主机通信
# nginx.conf - Docker环境下的负载均衡
upstream nodejs {
server app:3000;
}
server {
listen 80;
location / {
proxy_pass http://nodejs;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
5.2 Docker Swarm模式
# 初始化Swarm集群
docker swarm init --advertise-addr 192.168.1.100
# 其他节点加入集群
docker swarm join --token <token> 192.168.1.100:2377
# 部署服务(带负载均衡)
docker service create --name myapp \
--replicas 4 \
--publish 3000:3000 \
myapp:latest
# 查看服务
docker service ls
docker service ps myapp
# 扩容/缩容
docker service scale myapp=8
# 更新服务(滚动更新)
docker service update --image myapp:v2 myapp
六、性能优化
6.1 Node.js应用优化
// 1. 使用集群模式
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
// 工作进程
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
// 2. 使用gzip压缩
const compression = require('compression');
app.use(compression());
// 3. 使用连接池
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'myapp',
connectionLimit: 10, // 根据负载均衡节点数调整
queueLimit: 0
});
// 4. 缓存热点数据
const redis = require('redis');
const client = redis.createClient();
app.get('/api/data', async (req, res) => {
// 先查缓存
const cached = await client.get('hot-data');
if (cached) {
return res.json(JSON.parse(cached));
}
// 查数据库
const [rows] = await pool.query('SELECT * FROM hot_table LIMIT 100');
await client.setex('hot-data', 300, JSON.stringify(rows));
res.json(rows);
});
app.listen(port);
}
6.2 Nginx优化
# /etc/nginx/nginx.conf - 优化参数
# 工作进程数(通常等于CPU核心数)
worker_processes auto;
# 每个工作进程的最大连接数
events {
worker_connections 10240;
use epoll; # Linux高性能事件模型
multi_accept on;
}
http {
# 文件描述符缓存
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
# 缓冲区优化
client_body_buffer_size 128k;
client_max_body_size 10m;
client_header_buffer_size 1k;
# TCP优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 100;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript;
# 代理优化
proxy_buffering off;
proxy_buffer_size 16k;
proxy_busy_buffers_size 24k;
proxy_buffers 64 4k;
# 上游服务器配置
upstream backend {
keepalive 32; # 保持连接池
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
location / {
proxy_pass http://backend;
# 启用连接复用
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
七、监控与维护
7.1 PM2监控
# 实时监控
pm2 monit
# 查看详细信息
pm2 show myapp
# 查看日志
pm2 logs myapp --lines 100
# 清理日志
pm2 flush
# 安装PM2 Plus(商业监控)
pm2 plus
7.2 Nginx日志分析
# 访问日志分析
tail -f /var/log/nginx/access.log
# 统计QPS
watch -n 1 "wc -l /var/log/nginx/access.log | awk '{print \$1 \" requests\"}"
# 统计状态码
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 统计最频繁访问的URL
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 统计最慢的请求
awk '{print $NF " " $7}' /var/log/nginx/access.log | sed 's/"//g' | sort -rn | head -20
7.3 完整监控脚本
#!/bin/bash
# Node.js负载均衡监控脚本
# 1. 检查Node.js进程
check_node_processes() {
echo "===== Node.js进程状态 ====="
pm2 status
echo ""
}
# 2. 检查Nginx状态
check_nginx() {
echo "===== Nginx状态 ====="
systemctl status nginx --no-pager | grep Active
echo "Nginx连接数: $(netstat -an | grep :80 | wc -l)"
echo ""
}
# 3. 检查负载均衡后端
check_upstream() {
echo "===== 上游服务器状态 ====="
for port in 3000 3001 3002 3003; do
if curl -s http://127.0.0.1:$port/health > /dev/null; then
echo "端口 $port: ✅ 正常"
else
echo "端口 $port: ❌ 不可用"
fi
done
echo ""
}
# 4. 检查系统资源
check_system() {
echo "===== 系统资源 ====="
echo "CPU使用率: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}')"
echo "内存使用: $(free -h | grep Mem | awk '{print $3 "/" $2}')"
echo "磁盘使用: $(df -h / | tail -1 | awk '{print $5}')"
echo ""
}
# 5. 检查网络连接
check_network() {
echo "===== 网络连接 ====="
echo "ESTABLISHED连接数: $(netstat -an | grep ESTABLISHED | wc -l)"
echo "TIME_WAIT连接数: $(netstat -an | grep TIME_WAIT | wc -l)"
echo ""
}
# 执行所有检查
check_node_processes
check_nginx
check_upstream
check_system
check_network
# 6. 性能测试(可选)
echo "===== 性能测试(10秒)====="
ab -n 1000 -c 100 http://127.0.0.1/ || echo "请安装apache2-utils: sudo yum install -y httpd-tools"
八、常见问题与解决方案
Q1:负载均衡后会话(Session)丢失怎么办?
// 解决方案一:使用Redis存储Session
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const redisClient = redis.createClient({
host: '127.0.0.1',
port: 6379
});
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret',
resave: false,
saveUninitialized: false,
cookie: { secure: false }
}));
# 解决方案二:使用IP哈希保持会话
upstream backend {
ip_hash;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
Q2:如何平滑重启(零停机)?
# PM2零停机重启
pm2 reload myapp
# 或逐个重启(更精细控制)
pm2 restart myapp --only app-3000
sleep 5
pm2 restart myapp --only app-3001
# ...
# Nginx平滑重载配置
sudo nginx -t && sudo systemctl reload nginx
Q3:如何排查负载不均衡?
# 1. 查看Nginx访问日志,分析请求分布
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c
# 2. 检查上游服务器权重配置
grep "server.*weight" /etc/nginx/conf.d/*.conf
# 3. 检查服务器负载
for port in 3000 3001 3002 3003; do
echo "Port $port CPU使用率:"
top -b -n1 -p $(pgrep -f "port $port") | grep node
done
九、总结
Node.js在CentOS上实现负载均衡的完整方案:
- 单机多核:使用PM2集群模式
- 多机负载:使用Nginx反向代理
- 容器化:使用Docker Compose或Swarm
- 高可用:配置Keepalived+Nginx主备
- 监控维护:使用PM2 monit+Nginx日志分析
# 快速启动命令汇总
# 1. PM2集群模式
pm2 start app.js -i max --name "myapp"
# 2. Nginx负载均衡
sudo systemctl start nginx
# 3. 查看状态
pm2 status && systemctl status nginx
# 4. 监控
pm2 monit
注:本文基于Node.js 20.x和Nginx 1.24编写,具体配置可能因版本而异。