引子:
最近在调试一个nodejs的工程,这个工程是由国外team维护的,我们可能需要了解其内部的一些关于token运行机制。由于暂时没有权限获得源码,只有一个已经部署好服务的测试环境供我做research,所以我唯一想到的就是用node-inspector这个debugger工具来远程调试一下代码以方便调研。
但是整个过程却没有想象中顺利,当中躺了一些坑。本文就是介绍我遭遇的经历以及我的解决这些问题的思路,让大家以后能顺利的debug较为复杂nodejs程序。
问题1 node-inspector似乎没有work,chrome获取不到代码
按照以往经验,只需要在启动server.js的地方加上 --debug参数,然后运行node-inspector 便能,在chrome中输入 http://service_domain:8080/?port=5858进行断点调试了。
如
node --debug start.js
但是这么做chrome 访问http://service_domain:8080/?port=5858 木有显示出源码,当中必有蹊跷。
于是我查看了 start.js, 原来奥秘就在其中,start.js并不是一个具体的服务,经过层层剖析,它实际上最终运行了这样一份代码
const forever = require('forever');
const path = require('path');
const cwd = process.cwd();
module.exports = {
start:function(){
var logFile = path.resolve(cwd,'log/forever_log.log');
var script = path.resolve(cwd,'kluster.js');
forever.startDaemon(script,{
silent: false,
uid:'demo',
append: false,
max: 1,
logFile: logFile,
});
}
};
观察了一下似乎找到原因了,实际上真正的script是由forever这个工具来启动的,我们可以看到forever.startDaemon(script,opts}中的script才是真正的服务脚本,于是查阅了forever和forever-monitor的文档,发现需要在opts中加--debug的启动参数。于是我就添加了command:"node --debug" 这一句来启动真正的服务。
forever.startDaemon(script,{
silent: false,
uid:'demo',
append: false,
max: 1,
logFile: logFile,
command: 'node --debug'
});
好,这个时候再次运行node-inspector后,令人激动的场景出现了,服务端的code显示在chrome中,正当我以为一切都搞定的时候,下一个坑又出现了。
问题2 node-inspector已经成功显示了源码,但是加了breakpoint后却hit不了
这个问题让我百思不得其姐,我甚至都放弃了node-inspector,而改用node原生的在代码中添加debugger语句的方式来调试代码都不成功,断点始终是失效的。于是只好再深入剖析代码了。
var script = path.resolve(cwd,'kluster.js');
之前使用forever启动的Kluster.js这份文件必有蹊跷。
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
const http = require('http');
if (cluster.isMaster) {
console.log("master start...");
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('listening', function(worker, address) {
console.log('listening: worker ' + worker.process.pid + ', Address: ' + address.address + ":" + address.port);
});
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(3000);
}
原来项目中使用了cluster这个module来进行了负载均衡,也就是说nodejs会起n个子进程来同时监听3000端口,我们也可以查看下node-inspector的log输出
Debugger listening on port 5858
master start...
Debugger listening on port 5859
Debugger listening on port 5860
Debugger listening on port 5861
Debugger listening on port 5862
listening: worker 9968, Address: null:3000
listening: worker 12588, Address: null:3000
listening: worker 9128, Address: null:3000
listening: worker 10856, Address: null:3000
终于真相大白了,原来5858监听的是master进程,进程上并没有跑实质的service所以导致断点截获不了
只要将http://service_domain:8080/?port=5858中的port改成5859~5862中的任意一个端口就可以监听子进程了。果然断点似乎是可以断住了。
http://service_domain:8080/?port=5860
问题3 断点有时生效有时不生效
改变port端口为5060后发现断点可以断住了,但是又有一个新问题产生了,那就是有时候断点仍然会失效,这个问题就很好分析了,因为负载均衡时服务器将请求分配给某个子进程是不固定的,也就是说我页面上发出的request有可能分配给5859端口上的worker1来处理,也有可能被分配个其它端口上的worker来处理。所以光监听5860端口还是不够的。难道我们需要开4个chrome监听所有的端口嘛?这样太过麻烦,那么我们不妨就来做减法,减少子进程的数量,把它控制在1条子进程,这样我们就只需要监听5859一个端口就可以截获所有的request的请求了。
const cluster = require('cluster');
//const numCPUs = require('os').cpus().length;
const numCPUs = 1;
至此,我们就可以使用node-inspector愉快地调试我们的业务代码了。
这里给出本文示例的code: https://github.com/momoko8443/nodejs_demo_area
参考文献:
解读Nodejs多核处理模块cluster
node.js debugging with node-inspector and forever.js
What’s New in Node.js v0.12: Debugging Clustered Apps with Node-Inspector
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。