Appearance
Node.js 部署与运维
问题
Node 应用上线为什么常用 pm2?它解决了哪些问题?线上日志怎么收集、多实例怎么做负载均衡?一个 Node 服务要稳定运行,需要哪些服务治理能力?
结论
理解路径
Node 主线程是单线程,进程一崩整个服务就没了,所以「上线」要解决三件事:进程别挂(守护与自愈)、把多核和多机用起来(cluster / 负载均衡)、出问题能看见能兜底(日志、监控、优雅启停)。pm2 负责前两件的单机部分,Nginx / 注册中心等负责多机部分。
为什么用 pm2 部署?
直接 node app.js 跑在前台,进程一崩、终端一关服务就没了。pm2 是 Node 生态最常用的进程管理器,主要给三层能力:
- 守护与自愈:以 daemon 方式托管进程,崩溃自动重启,机器重启后也能拉起,避免单点失联。
- 多核利用:
-i开 cluster 模式,按 CPU 核数 fork 多个 worker 共享端口,把主线程单线程的瓶颈摊开。 - 零停机重载:
reload逐个替换 worker,新进程起来再杀老进程,发布过程中始终有实例在服务,不掉请求。
bash
pm2 start app.js -i max # 按 CPU 核数启动 cluster 模式
pm2 reload app # 零停机重载(区别于 restart 的硬重启)
pm2 logs # 查看实时日志
pm2 startup && pm2 save # 开机自启 + 保存当前进程列表容器化部署时不用 pm2 start(守护和重启交给 K8s / Docker),而是用 pm2-runtime app.js 跑在前台,让编排系统接管进程生命周期。
线上日志和负载均衡怎么做?
日志:生产不能只靠 console.log。用 winston 或 pino(性能更好)做结构化日志,按级别输出到文件,再由 Filebeat / 采集 agent 收集,汇总到 ELK(Elasticsearch + Logstash + Kibana)或 Loki 这类集中式系统,便于检索和告警。
负载均衡:按规模分层:
| 层级 | 方案 | 说明 |
|---|---|---|
| 单机多核 | pm2 cluster / cluster 模块 | 主进程按核数把连接分发给多个 worker |
| 多机七层 | Nginx / 应用网关 | 按 HTTP 反向代理分发到多台机器,可做路径路由、灰度 |
| 多机四层 | LVS / 云 SLB | 传输层分发,吞吐高,常架在 Nginx 之前 |
多实例后要注意无状态:session 不能存在单个进程的内存里,否则请求被分发到别的实例就丢了,应放到 Redis 等共享存储;本地文件、内存缓存同理。
Node 服务治理需要哪些能力?
服务一旦变成多实例、被其它服务依赖,就需要一套治理能力保证稳定:
- 注册与发现:实例启动后注册到注册中心(Consul / Nacos / etcd),调用方动态发现可用节点,而不是写死 IP。
- 配置中心:配置集中管理、动态下发,改配置不必重启发布。
- 路由与负载均衡:在发现的节点间按策略(轮询 / 权重 / 最少连接)分发。
- 容错:超时、重试、熔断(依赖故障时快速失败,避免雪崩)、限流(保护自身不被流量打垮),Node 常用
opossum做熔断。 - 链路追踪:贯穿一次调用的 traceId,跨服务串起日志,定位慢在哪一环。
- 监控告警:采集 QPS、延迟、错误率、内存等指标,超阈值告警。
- 优雅启停:收到
SIGTERM先停止接收新请求、处理完存量请求再退出,配合负载均衡先摘流量,避免发布时丢请求。
js
// 优雅关闭:先停止接新连接,存量请求处理完再退出
process.on('SIGTERM', () => {
server.close(() => {
// 关闭数据库连接等资源后退出
process.exit(0)
})
})小团队不一定全套上微服务治理,但「优雅启停 + 日志监控 + 限流熔断」是 Node 服务上线的基本盘。