node.js 应用启动时出现以下错误:
Error: Cannot find module '../build/Debug/addon' at Function.Module._resolveFilename (module.js:339:15) at Function.Module._load (module.js:290:25) at Function._load (/usr/lib/node_modules/pm2/node_modules/pmx/lib/transaction.js:62:21) at Module.require (module.js:367:17) at require (internal/module.js:16:19) at Object.<anonymous> (node_modules/heapdump/lib/main.js:18:15) at Module._compile (module.js:413:34) at Object.Module._extensions..js (module.js:422:10) at Module.load (module.js:357:32) at Function.Module._load (module.js:314:12)
改了一下 heapdump/lib/main.js:18:15 附近的代码,输出了真正的错误信息:
Error: Module version mismatch. Expected 47, got 46.
根据 node_version.h 中 NODE_MODULE_VERSION 的定义,46 对应 Node.js v4.0.0,47 对应 Node.js v5.0.0。应该是编译 heapdump 模块使用的 Node.js 版本和运行时 Node.js 版本不一致,编译时我通过 $PATH 环境变量,将 Node.js v4.2.3 置为默认的 Node.js 版本了。
$ /usr/bin/node --version v5.7.0 $ node --version v4.2.3
我使用的是 pm2 做为进程管理器,cluster 模式运行 node.js 应用,pm2 后台进程使用的是默认版本的 Node.js 版本(v5.7.0)启动,应该是 pm2 也使用同样的 Node.js 版本(v5.7.0)来运行应用,执行 pm2 save 后,~/.pm2/dump.pm2 中我的应用的 $PATH 是正确的,已经将 Node.js v4.2.3 置为默认的 Node.js 版本,不知为何 pm2 并未采用。
pm2 分为前端命令和后端 daemon 两部分,真正的操作都是由 daemon 来施行,当我们使用 pm2 start 来启动 app 时,只是把命令通过 unix socket 传递给了 daemon,一个合理的猜想是 pm2 命令并没有把当前 shell 的 $PATH 传递给 daemon,或者是 daemon 创建 app 进程时传递过来的 $PATH 设置未生效。
查看当前的 pm2 版本:
$ ps aux | grep PM2 | grep -v grep tangxin+ 17538 0.1 0.4 1185564 32020 ? Ssl 2月25 9:29 PM2 v0.14.5: God Daemon
通过 –interpreter 选项启动应用时指定 Node.js v4.2.3: –interpreter=/usr/local/node-v4.2.3/bin/node
通过 pm2 delete 删除应用后再 start 应用,结果还是一样的错误,查看应用实际使用的 node 版本:
$ ls -la /proc/22387/exe lrwxrwxrwx 1 tangxinfa tangxinfa 0 3月 2 14:00 /proc/22387/exe -> /usr/bin/node
使用的还是系统默认的 Node.js 版本 v5.7.0。
经过测试,可以确认:
通过 –interpreter 指定其它 node 版本,在 cluster 模式下无效,fork 模式下有效。
参见相关 Issues:
- interpreter ignored when using cluster mode · Issue #1575 · Unitech/pm2
- Using different versions of node via nvm for each app · Issue #1034 · Unitech/pm2
- –interpreter not applied? · Issue #1224 · Unitech/pm2
查看 pm2 与 node.js 的源代码进一步确认该问题:
pm2 调用 cluster.fork 创建工作进程
引用自 pm2/lib/God/ClusterMode.js
cluster.fork({pm2_env: JSON.stringify(env_copy)})
cluster.fork 调用 child_process.fork 创建工作进程
引用自 node/lib/cluster.js
return fork(cluster.settings.exec, cluster.settings.args, { env: workerEnv, silent: cluster.settings.silent, execArgv: execArgv, gid: cluster.settings.gid, uid: cluster.settings.uid });
根据 child_process.fork 的实现(见 node/lib/child_process.js),由于未传入
execPath
选项,会使用process.execPath
的值,也就是会使用 pm2 后台进程的 node 可执行程序路径来创建工作进程。
应该可以通过指定不同的 $PM2_HOME 环境变量,跑多套 pm2,各个 pm2 使用不同版本的 Node.js,多个 cluster 模式的 pm2 应用也就会使用不同版本 Node.js。