如题,因为npmjs上的第三方库良莠不齐,时不时会遇到各种问题,以我自己的经历为例。产品1月底上线,我现在都要从公司离职了,直到昨天才把内存泄露彻底排查清楚。回想最初发布的时候server莫名其妙crash,一直到排查清楚,那无数个担惊受怕的周末和夜晚,顿感心有余悸啊。
扯淡完毕,接下来分享一下我的土办法,抛砖引玉,欢迎大家分享更好的调试方法。我是这么干的: 测试server端 node-dev + memwatch,每次内存回收时观察内存使用情况,然后从另一台机器写一个脚本不断发送请求(所有API都cover),然后检查server端的输出情况。定位到有泄漏的API后,进去一点点排查,方法就是(太土了,简直不好意思说)提前return callback,就跟查哪段电线短路似的,我在这设一下,没有泄露,再往后一点段设置一下,有泄漏,就说明这之间的代码有问题,最终定位到有泄漏代码。
发现的泄漏都是出于第三方模块:
-
ZeroRPC,前dotCloud公司(没错,就是现在红遍全球的Docker公司的前身)开发的基于ZeroMQ的远程调用框架,后来没人维护了,npm上的版本有严重的内存漏洞。后来去github上提,没回复,最后直接给作者发邮件问了,然后说作者说问一下Docker的人。然后就没消息了。直到发帖前又查了一下,发现最近这个项目又重新有人维护了,4天前迁移到了新的github项目。内存泄露的具体原因可以参考issue https://github.com/0rpc/zerorpc-node/issues/57#event-323473247
-
Mongoose,3.x的版本,具体记不清排查的时候是哪些版本了,执行find,findOne的时候,如果文档是个比较大的对象,会导致内存泄露。所以执行查询的时候最好指定必须的字段,这样查询速度更快。另外发现直接用collection查询更快一些,例如有一个collection的model定义为Book:
// The code is faster than Book.find(...)
Book.collection.find( { published_date : { $gt : new Date( 2015, 0, 1 ) } } )
所以如果仅仅是查询,又不需要用到Mongoose封装的一些高级功能,可以直接用collection查询
- underscore 某些版本的underscore在执行大量的each时,会导致轻微的内存增加,不算泄露吧,但是比直接for循环要使用更多的内存。