为什么需要进程管理器?
当你的 Node.js 或 Python Web 应用上线后,单一进程跑在服务器上十分脆弱:代码未捕获异常导致进程退出、多核 CPU 资源浪费、部署更新时服务中断——这些都需要一个健壮的进程管理器来解决。PM2 和 Gunicorn 分别是 Node.js 与 Python 生态中最成熟的答案,但它们的理念和使用方式截然不同。本文会从真实部署场景出发,手把手演示两者的实战配置,并指出容易踩的坑。
PM2:Node.js 进程管理利器
安装与基础启动
PM2 需要一个全局安装:
npm install pm2 -g
启动一个 Express 应用:
pm2 start app.js --name my-app
此时 PM2 会在后台守护进程,即使终端关闭应用也不会停止。但你很快会发现,单进程无法利用多核 CPU——PM2 的 cluster 模式正是为此设计。
Cluster 模式与负载均衡
PM2 内置 cluster 模块,可以 fork 多个工作进程并自动轮询分发请求。配置方式有两种:
- 命令行:
pm2 start app.js -i max(max 表示使用所有 CPU 核心) - 配置文件 ecosystem.config.js:
module.exports = {
apps: [{
name: 'my-app',
script: 'app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
}
}]
};
使用负载均衡前,必须确保应用是无状态的(Session 不能存入本地内存,推荐用 Redis)。否则请求分发到不同进程会导致登录状态丢失。
优雅重启与零宕机部署
当代码更新需要重启时,直接 kill 进程会造成正在处理的请求中断。PM2 提供 pm2 reload 命令实现优雅重启:先创建新进程,待新进程就绪后再关闭旧进程,保证服务不中断。
实战中你可能会遇到 优雅关闭的陷阱:如果应用没有监听 SIGINT 信号并主动释放数据库连接,PM2 强制 kill 会导致连接泄漏。正确做法是在应用中加入:
process.on('SIGINT', () => {
server.close(() => {
console.log('HTTP server closed');
process.exit(0);
});
});
日志管理与轮转
PM2 默认将所有日志输出到 ~/.pm2/logs,长时间运行会撑爆磁盘。推荐配置日志轮转:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
或者直接在 ecosystem.config.js 中指定日志路径,并配合 Linux 的 logrotate 工具。
监控与自愈
PM2 内置简易监控:pm2 monit 可以查看 CPU/内存占用。更专业的做法是结合 Keymetrics(PM2 的 SaaS 监控平台)或 Prometheus 指标导出。但自愈能力是默认开启的——任何 worker 异常退出后 PM2 会立即重启,你无需额外配置。
Gunicorn:Python WSGI 服务器实战
安装与基本工作模型
对于 Flask/Django 应用,Gunicorn 是最常用的生产级 WSGI 服务器:
pip install gunicorn
启动方式:
gunicorn -w 4 myapp:app
-w 4 表示 4 个工作进程。Gunicorn 默认使用 pre-fork 模型:主进程负责监听端口,当请求到来时,通过预先 fork 出的 worker 进程处理。worker 有多种类型:
- sync(默认):同步阻塞,适合 CPU 密集型或短链接场景,但并发能力差
- gevent:基于协程,适合高并发 I/O 密集型应用
- uvicorn:支持 ASGI(异步 Python Web 框架如 FastAPI)
Worker 数量与并发策略
Worker 数量的计算公式业界推荐 2 * CPU核心数 + 1,但需根据应用负载微调。如果使用 sync worker,每个 worker 一次只能处理一个请求,并发取决于 worker 数量;改用 gevent worker 则可在一个 worker 内处理上千个协程并发:
gunicorn -k gevent -w 4 --worker-connections 1000 myapp:app
注意:使用 gevent 前务必 monkey patch,否则标准库 socket 会阻塞所有协程。在应用入口添加:
from gevent import monkey
monkey.patch_all()
配置实战:Gunicorn 配置文件的写法
命令行参数多时容易遗漏,建议使用配置文件 gunicorn.conf.py:
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gevent"
worker_connections = 1000
timeout = 120
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
启动时直接指定:gunicorn -c gunicorn.conf.py myapp:app
优雅重启与信号控制
Gunicorn 没有类似 PM2 reload 的内置零宕机更新功能。你需要通过 USR2 信号 实现平滑重启:
kill -HUP [master_pid] # 重载配置文件(不重启 worker)
kill -USR2 [master_pid] # 重启所有 worker(先启新再关旧)
但要注意,HUP 信号在不同版本行为不一致,USR2 才是真正实现优雅重启的方式。生产部署时建议配合 systemd 或 supervisor,而不是手动发送信号。
日志、监控与 systemd 集成
Gunicorn 的日志管理需要自行处理轮转,通常配合 logrotate。监控方面可以集成 Prometheus 客户端 暴露指标,或者使用 Datadog、New Relic 等 APM。更简单的做法是交给 systemd:
[Unit]
Description=Gunicorn instance for myapp
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/app
ExecStart=/path/to/venv/bin/gunicorn -c gunicorn.conf.py myapp:app
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
这样即使 worker 意外崩溃,systemd 也会自动拉起,同时内存和 CPU 限制可通过 systemd 配置。
PM2 vs Gunicorn:差异与适用场景
| 维度 | PM2 | Gunicorn |
|---|---|---|
| 语言生态 | Node.js | Python(WSGI/ASGI) |
| 集群模式 | 内置 cluster,零配置 | 需配合 nginx 反向代理实现负载均衡,或使用 uWSGI 加 –processes |
| 零宕机部署 | pm2 reload 内置 | 需手动发送 USR2 信号或借助 nginx 双部署 |
| 日志管理 | 自带日志轮转插件 | 需外部工具(logrotate) |
| 监控 | pm2 monit + Keymetrics | 无内置,依赖第三方或 Prometheus |
| 进程守护 | 默认开启 | 需 systemd/supervisor |
简单判断:如果你的项目是 Node.js,PM2 是首选;如果是 Python Flask/Django,Gunicorn 搭配 systemd 足够稳定;对于 FastAPI 等异步框架,考虑 Uvicorn + Gunicorn(with uvicorn worker)。
生产环境避坑指南
误区一:pm2 start 后就不管了
PM2 在系统重启后不会自动启动,需要设置 pm2 startup 生成 systemd 单元。否则机器重启后应用就挂了。
误区二:Gunicorn 的 --preload 不加
不加 --preload 时每个 worker 都会重新加载应用代码,会浪费大量内存(尤其深度学习模型加载场景)。加上该选项可以让应用在 master 进程加载后再 fork worker,减少内存开销。
误区三:忽略文件描述符限制
高并发下 Gunicorn 或 PM2 可能因为系统限制的 open files 过小导致崩溃。务必在 /etc/security/limits.conf 中调大:
* soft nofile 65535
* hard nofile 65535
总结
无论是 PM2 还是 Gunicorn,核心目标都是让 Web 应用稳定、高效、易运维。PM2 开箱即用,适合快速部署;Gunicorn 更轻量,需配合系统服务管理。选择的关键在于你使用的语言和技术栈,以及团队对运维复杂度的接受程度。实践才能出真知,建议在测试环境完整走一遍“部署 → 压力测试 → 日志查看 → 优雅重启”全流程,再决定生产环境方案。
延伸阅读
