您现在的位置是:网站首页 > 负载均衡与集群部署文章详情
负载均衡与集群部署
陈川
【
Node.js
】
20700人已围观
5370字
负载均衡与集群部署是提升Express应用性能和可靠性的关键手段。通过合理配置,可以有效分摊服务器压力,避免单点故障,确保应用在高并发场景下的稳定运行。
负载均衡的基本概念
负载均衡的核心思想是将网络请求分发到多个服务器节点,避免单个服务器过载。常见的负载均衡算法包括:
- 轮询(Round Robin):按顺序将请求分配给服务器
- 最少连接(Least Connections):将请求分配给当前连接数最少的服务器
- IP哈希(IP Hash):根据客户端IP地址分配服务器
// 简单的轮询负载均衡示例
const servers = ['http://server1:3000', 'http://server2:3000', 'http://server3:3000'];
let current = 0;
function getServer() {
const server = servers[current];
current = (current + 1) % servers.length;
return server;
}
Express集群部署的实现方式
Node.js是单线程的,但可以通过集群模块充分利用多核CPU。Express应用可以通过以下方式实现集群:
使用Node.js原生cluster模块
const cluster = require('cluster');
const os = require('os');
const express = require('express');
if (cluster.isMaster) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
const app = express();
app.get('/', (req, res) => {
res.send(`Hello from worker ${process.pid}`);
});
app.listen(3000, () => {
console.log(`Worker ${process.pid} started`);
});
}
使用PM2进程管理器
PM2提供了更简单的集群管理方式:
pm2 start app.js -i max
负载均衡器的配置
Nginx配置示例
http {
upstream express_servers {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
location / {
proxy_pass http://express_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
HAProxy配置示例
frontend http-in
bind *:80
default_backend express_servers
backend express_servers
balance roundrobin
server server1 127.0.0.1:3000 check
server server2 127.0.0.1:3001 check
server server3 127.0.0.1:3002 check
会话保持与状态共享
在集群环境中,需要特别注意会话状态的管理:
使用Redis存储会话
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({
host: 'redis-server',
port: 6379
}),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false
}));
使用JWT实现无状态认证
const jwt = require('jsonwebtoken');
app.post('/login', (req, res) => {
// 验证用户凭证
const token = jwt.sign({ userId: user.id }, 'secret-key', { expiresIn: '1h' });
res.json({ token });
});
app.get('/protected', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
try {
const decoded = jwt.verify(token, 'secret-key');
res.send(`Hello user ${decoded.userId}`);
} catch (err) {
res.status(401).send('Invalid token');
}
});
健康检查与故障转移
确保负载均衡器能够检测服务器状态:
Express健康检查端点
app.get('/health', (req, res) => {
// 检查数据库连接等关键资源
res.status(200).json({ status: 'healthy' });
});
Nginx健康检查配置
upstream express_servers {
server 127.0.0.1:3000;
server 127.0.0.1:3001 backup;
check interval=3000 rise=2 fall=3 timeout=2000;
}
静态资源与缓存优化
在集群环境中优化静态资源服务:
使用CDN分发静态资源
app.use('/static', express.static('public', {
maxAge: '1y',
setHeaders: (res, path) => {
if (path.endsWith('.css')) {
res.setHeader('Content-Type', 'text/css');
}
}
}));
集群间的缓存同步
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 3600 });
// 使用Redis发布/订阅同步缓存
redisSubscriber.on('message', (channel, message) => {
const { key, value } = JSON.parse(message);
cache.set(key, value);
});
function setClusterCache(key, value) {
cache.set(key, value);
redisPublisher.publish('cache-update', JSON.stringify({ key, value }));
}
日志收集与监控
集中管理集群中各节点的日志:
使用Winston进行日志收集
const winston = require('winston');
const { Loggly } = require('winston-loggly-bulk');
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new Loggly({
token: 'your-loggly-token',
subdomain: 'your-subdomain',
tags: ['express-cluster'],
json: true
})
]
});
app.use((req, res, next) => {
logger.info(`${req.method} ${req.url}`);
next();
});
使用Prometheus监控指标
const promBundle = require('express-prom-bundle');
const metricsMiddleware = promBundle({
includeMethod: true,
includePath: true,
customLabels: { cluster_node: process.pid }
});
app.use(metricsMiddleware);
部署策略与蓝绿发布
实现零停机部署:
使用Nginx实现蓝绿部署
upstream blue {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
upstream green {
server 127.0.0.1:4000;
server 127.0.0.1:4001;
}
# 通过变量切换流量
set $deployment "blue";
server {
location / {
if ($deployment = "blue") {
proxy_pass http://blue;
}
if ($deployment = "green") {
proxy_pass http://green;
}
}
}
使用Kubernetes进行滚动更新
apiVersion: apps/v1
kind: Deployment
metadata:
name: express-app
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
containers:
- name: express
image: your-image:v2
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5