参考链接:http://www.barretlee.com/blog/2015/10/07/debug-nodejs-in-command-line/
NodeJS 给我们提供了 Debugger 模块,内建客户端,通过 TCP 将命令行的输入传送到内建模块以达到调试的目的。
在启动文件时,添加第二个参数 debug:
bcaring:bqxu.github.io bcaring$ node debug assets/node/2016-09/debugger.js
< Debugger listening on port 5858
connecting to 127.0.0.1:5858 ... ok
break in assets/node/2016-09/debugger.js:1
> 1 'use strict';
2 let i = 0;
3
debug>
调试代码的时候存在两个状态,
一个是操作调试的位置,比如下一步,进入函数,跳出函数等,此时为 debug 模式;
另一个是查看变量的值,比如进入循环中,想查看循环计数器 i 的值,此时为 repl(read-eval-per-line) 状态
在 debug 模式下输入 repl 即可进入 repl 状态:
debug> repl
Press Ctrl + C to leave debug repl
> process
{ title: '/usr/local/bin/node',
version: 'v6.2.0',
moduleLoadList:
...
按下 Ctrl+C 可以从 repl 状态回到 debug 状态下,
debug 状态下有多少调试命令,执行 help 即可:
debug> help
Commands: run (r), cont (c), next (n), step (s), out (o), backtrace (bt), setBreakpoint (sb), clearBreakpoint (cb),
watch, unwatch, watchers, repl, exec, restart, kill, list, scripts, breakOnException, breakpoints, version
常用的命令
命令 | 解释 |
cont, c | 进入下一个断点 |
next, n | 下一步 |
step, s | 进入函数 |
out, o | 跳出函数 |
setBreakpoint(), sb() | 在当前行设置断点 |
setBreakpoint(line), sb(line) | 在 line 行设置断点 |
当我们使用 debug 参数打开一个 node 文件时,会输出这样一行文案:
Debugger listening on port 5858
可以访问下 http://localhost:5858
除了在命令行中直接调试之外,我们还可以通过另外两种方式去调试这个代码:
如果我们使用 –debug 参数打开文件:
此时,nodejs 不会进入到命令行模式,而是直接执行代码,但是依然会开启内建调试功能,这就意味着我们具备了远程调试 NodeJS 代码的能力,使用 –debug 参数打开服务器的 nodejs 文件,然后通过:
node debug <服务器IP>:<调试端口,默认5858>
可以在本地远程调试 nodejs 代码。
不过需要区分下 –debug 和 –debug-brk, 前者会执行完所有的代码,一般是在监听事件的时候使用, 而后者,不会执行代码,需要等到外部调试接入后,进入代码区。
默认端口号是 5858,如果这个端口被占用,程序会递增端口号,我们也可以指定端口:
NodeJS 提供的内建调试十分强大,它告诉 V8,在执行代码的时候中断程度,等待开发者操控代码的执行进度。我们熟知的 node-inspector 也是用的这个原理。
node-inspector --web-port 8080 --debug-port 5858
这里的 –web-port 是 Chrome Devtools 的调试页面地址端口,–debug-port 为 NodeJS 启动的内建 debug 端口,我们可以在 http://localhost:8080/debug?port=5858 打开页面,调试使用 –debug(-brk) 参数打开的程序。
略
首先来分析下问题,内存飙高存在哪些方面的因素呢:
如何去分析 GC 的日志?
在启动程序的时候添加 –trace_gc 参数,V8 在进行垃圾回收的时候,会将垃圾回收的信息打印出来:
node --trace_gc app.js
更多启动选项:
启动项 | 含义 |
–max-stack-size | 设置栈大小 |
–v8-options | 打印 V8 相关命令 |
–trace-bailout | 查找不能被优化的函数,重写 |
–trace-deopt | 查找不能优化的函数 |
这些启动项都可以让我们查看 V8 在执行时的各种 log 日志,对于排查隐晦问题比较有用。
然而这堆日志并不太好看,我们可以将日志输出来之后交给专业的工具帮我们分析,
相比很多人都用过 Chrome DevTools 的 JavaScript CPU Profile,
通过 Profile 可以找到具体函数在整个程序中的执行时间和执行时间占比,
从而分析到具体的代码问题,V8 也提供了 Profile 日志导出:
node --prof app.js
执行命令之后,会在该目录下产生一个 *-v8.log 的日志文件, 我们可以安装一个日志分析工具 tick:
sudo npm install tick -g
node-tick-processor *-v8.log
在设计上,Node.js是单线程的。 为了能让一个单线程处理许多并发的请求,你可以永远不要让线程等待阻塞,同步或长时间运行的操作。 Node.js的一个显著特征是:它从上到下的设计和实现都是为了实现异步。这让它非常适合用于事件型程序。 不幸的是,还是有可能会发生同步/阻塞的调用。 例如,许多文件系统操作同时拥有同步和异步的版本,比如writeFile和writeFileSync。 即使你用代码来控制同步方法,但还是有可能不注意地用到阻塞调用的外部函数库。 当你这么做时,对性能的影响是极大的。
我们的初始化log在实现时无意地包含了一个同步调用来将内容写入磁盘。 如果我们不做性能测试那么就会很容易忽略这个问题。 当以developer box中一个node.js实例来作为标准测试,这个同步调用将导致性能从每秒上千次的请求降至只有几十个。
Node.js的http客户端会自动地使用套接字池: 默认地,它会限制每台主机只能有5个套接字。 虽然套接字的重复使用可能会让资源的增加在控制之下, 但如果你需要处理许多数据来自于同一主机的并发请求时, 将会导致一系列的瓶颈。 在这种情况下,增大maxSockets 的值或关闭套接字池是个好主意:
// Disable socket pooling
var http = require('http');
var options = {.....};
options.agent = false;
var req = http.request(options)
许多服务器和客户端支持gzip来压缩请求和应答。 无论是应答客户端还是向远程服务器发送请求,请确保充分使用它。
试着让你所有的阻塞操作-向远程服务发送请求,DB调用,文件系统访问并行化。 这将能减少最慢的阻塞操作的等待时间,而不是所有阻塞操作的等待时间。 为了保持回调和错误处理的干净,我们使用Step来控制流量。
许多express的例子都包含如下的配置: app.use(express.session({ secret: “keyboard cat” })); 默认地,session数据是存储在内存中的,这会给服务器增加巨大的开销,特别是随着用户量的增长。
你可以使用一个外部session存储,比如MongoDB或Redis,不过每一个请求将会导致远程调用来取得session数据的开销。 在可能的情况下,最好的选择就是在服务器端存储所有的无状态数据。 通过不包含上述express配置让session自由化,你会看到更好的性能。
如果可能,用二进制模块取代JavaScript模块。
// Use built in or binary modules
var crypto = require('crypto');
var hash = crypto.createHmac("sha1",key).update(signatureBase).digest("base64");