Ubuntu下Node.js运行慢的排查与优化清单
一 快速定位瓶颈
- 资源与依赖
- 查看系统资源:用htop/glances观察CPU、内存、I/O是否吃紧;检查磁盘是否为SSD;确认Node.js版本是否过旧,优先使用LTS并通过nvm管理版本。
- 应用内快速指标
- 在代码中定期打点记录process.memoryUsage()、process.cpuUsage(),输出到日志,定位是否存在内存持续增长或CPU长时间占用。
- 负载与延迟
- 用ApacheBench、wrk、Autocannon做基线压测,观察P95/P99延迟与RPS随并发的变化,确认是吞吐不足还是尾延迟过高。
- CPU热点与调用栈
- 使用node --prof采集CPU profile,再用node --prof-process分析;或用node --inspect配合Chrome DevTools做火焰图定位慢函数。
- 内存与泄漏
- 借助heapdump在可疑时刻落盘堆快照,用Chrome DevTools Memory面板分析Retained Size与对象引用链。
二 系统层面优化
- 文件描述符与内核网络
- 提升进程可打开文件数:执行ulimit -n 65535,并在**/etc/security/limits.conf**中持久化;
- 调优网络队列与端口复用:在**/etc/sysctl.conf中设置如net.core.somaxconn=4096、net.ipv4.tcp_max_syn_backlog=4096、net.ipv4.ip_local_port_range=1024 65535、net.ipv4.tcp_tw_reuse=1、net.ipv4.tcp_fin_timeout=30**,执行sudo sysctl -p生效。
- 存储与硬件
- 优先使用SSD,并视情况增加内存,减少磁盘与内存交换导致的抖动。
三 运行时与代码层面优化
- 充分利用多核
- 使用cluster模块或进程管理器PM2启动与CPU核心数相当的进程:如pm2 start app.js -i max,分摊负载、提升并发。
- 避免阻塞事件循环
- 全程使用异步I/O(如fs.promises、数据库驱动异步API),严禁同步阻塞;将CPU密集任务拆分到Worker Threads/子进程或采用任务队列。
- 大数据与I/O
- 使用Streams处理大文件/大响应,降低内存峰值与GC压力。
- 内存与V8
- 通过**–max-old-space-size**设置合适的堆上限,避免频繁GC或OOM;按需调整V8启动参数。
- 数据与缓存
- 为数据库查询建立索引、使用连接池、尽量批量操作;对热点数据使用内存缓存(Redis/Memcached)。
- 依赖与打包
- 清理未使用依赖、选择更轻量库、必要时进行依赖预构建/缓存以缩短启动与运行时的解析成本。
四 监控与压测闭环
- 进程与指标
- 用PM2进行进程守护与监控(如pm2 monitor),观察CPU、内存、重启次数;按需接入**日志库(winston/pino)**输出结构化日志。
- APM与可视化
- 接入New Relic/Datadog等APM,或使用Prometheus + Grafana自建指标大盘,跟踪请求耗时、错误率、吞吐等关键指标。
- 压测与回归
- 以ab/wrk/Autocannon建立回归压测脚本,在每次优化后对比P50/P95/P99与RPS,确保优化有效且不引入回退。
五 常见场景与对策
| 场景 |
典型症状 |
快速对策 |
| CPU密集型计算 |
事件循环卡顿、P95飙升 |
将计算拆分到Worker Threads/子进程或任务队列;用node --prof定位热点函数并优化算法 |
| 高并发I/O |
吞吐上不去、连接超时 |
启用cluster/PM2多进程;提升文件描述符与内核网络参数;用Nginx做反向代理与静态资源承载 |
| 数据库慢 |
请求延迟高、DB占用高 |
增加索引、使用连接池、批量操作;引入Redis缓存热点数据 |
| 内存泄漏/膨胀 |
RSS持续增长、GC频繁 |
用heapdump抓快照、Chrome分析引用链;减少全局/闭包引用,及时移除事件监听 |
| 依赖/构建慢 |
启动慢、安装慢 |
清理未使用依赖、选择轻量替代;使用镜像源与依赖缓存;必要时预构建 |