概要
node.jsでアプリを作成しているとメモリリーク(やCPUの負荷)に悩まされることが多い。
多くはスナップショットを撮ってボトルネックを見つけて改善することできたから、
ここではその方法を載せる。
スナップショットの結果はライブラリの関係上Chromeブラウザの開発者ツールで確認する。
環境
- OS
- Linux www4322gi 3.2.0-64-generic #97-Ubuntu SMP Wed Jun 4 22:04:21 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
- Node
- Library
事前準備
ここからnvmとnode.jsをインストールする。
構築
expressのテンプレート作成
make template1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $ vim package.json
{
"name" : "snapshot_test" ,
"version" : "0.0.1" ,
"dependencies" : {
"express" : "4.8.7" ,
"ejs" : "1.0.0" ,
"express-generator" : "4.8.0"
}
}
$ npm install
$ ls node_modules/.bin/
express
$ . /node_modules/ .bin /express -e -t ejs snapshot_test
$ ls snapshot_test/
app.js bin package.json public routes views
|
サンプルアプリ開発
package.json1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | $ cd snapshot_test
$ vim package.json
{
"name" : "snapshot_test" ,
"version" : "0.0.0" ,
"private" : true ,
"scripts" : {
"start" : "node ./bin/www"
},
"dependencies" : {
"express" : "~4.8.6" ,
"body-parser" : "~1.6.6" ,
"cookie-parser" : "~1.3.2" ,
"morgan" : "~1.2.3" ,
"serve-favicon" : "~2.0.1" ,
"debug" : "~1.0.4" ,
"ejs" : "~0.8.5" ,
"request" : "2.40.0" ,
"cheerio" : "0.17.0"
}
}
$ npm update
|
routes/index.js
routes/index.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | var express = require( 'express' );
var router = express.Router();
var request = require( 'request' );
var cheerio = require( 'cheerio' );
router.get( '/' , function (req, res) {
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(body);
var hrefs = [];
$( 'a' ).each( function () {
hrefs.push($( this ).attr( 'href' ));
});
var result = {}
result.status = 'success' ;
result.contents = hrefs;
res.send(JSON.stringify(result));
} else {
var result = {}
result.status = 'error' ;
result.contents = '' ;
res.send(JSON.stringify(result));
}
})
});
module.exports = router;
|
スナップショット部分の開発
package.json1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $ vim package.json
{
"name" : "snapshot_test" ,
"version" : "0.0.0" ,
"private" : true ,
"scripts" : {
"start" : "node ./bin/www"
},
"dependencies" : {
"express" : "~4.8.6" ,
"body-parser" : "~1.6.6" ,
"cookie-parser" : "~1.3.2" ,
"morgan" : "~1.2.3" ,
"serve-favicon" : "~2.0.1" ,
"debug" : "~1.0.4" ,
"ejs" : "~0.8.5" ,
"request" : "2.40.0" ,
"cheerio" : "0.17.0" ,
"nodegrind" : "0.1.8" ,
"heapdump" : "0.2.10"
}
}
$ npm update
|
routes/snapcpu.js
routes/snapcpu.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | var express = require( 'express' );
var router = express.Router();
var fs = require( 'fs' );
var profiler = require( 'nodegrind' );
router.get( '/' , function (req, res) {
var name = ( new Date().getTime()) + '.cpuprofile' ;
profiler.startCPU(name);
setTimeout( function (){
var cpuProfile = profiler.stopCPU(name, 'cpuprofile' );
fs.writeFile(name, cpuProfile, function (err){
if (err) res.send(err);
else res.send( 'OK:' + name);
});
}, 5000);
});
module.exports = router;
|
routes/snapheap.js
routes/snapheap.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var express = require( 'express' );
var router = express.Router();
var fs = require( 'fs' );
var heapdump = require( 'heapdump' );
router.get( '/' , function (req, res) {
var name = ( new Date().getTime()) + '.heapsnapshot' ;
heapdump.writeSnapshot(name, function () {
res.send( 'OK:' + name);
});
});
module.exports = router;
|
ルーティングの設定
app.js
app.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | var express = require( 'express' );
var path = require( 'path' );
var favicon = require( 'serve-favicon' );
var logger = require( 'morgan' );
var cookieParser = require( 'cookie-parser' );
var bodyParser = require( 'body-parser' );
var routes = require( './routes/index' );
var users = require( './routes/users' );
var snapcpu = require( './routes/snapcpu' );
var snapheap = require( './routes/snapheap' );
var app = express();
app.set( 'views' , path.join(__dirname, 'views' ));
app.set( 'view engine' , 'ejs' );
app.use(logger( 'dev' ));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express. static (path.join(__dirname, 'public' )));
app.use( '/' , routes);
app.use( '/users' , users);
app.use( '/snapcpu' , snapcpu);
app.use( '/snapheap' , snapheap);
app.use( function (req, res, next) {
var err = new Error( 'Not Found' );
err.status = 404;
next(err);
});
if (app.get( 'env' ) === 'development' ) {
app.use( function (err, req, res, next) {
res.status(err.status || 500);
res.render( 'error' , {
message: err.message,
error: err
});
});
}
app.use( function (err, req, res, next) {
res.status(err.status || 500);
res.render( 'error' , {
message: err.message,
error: {}
});
});
module.exports = app;
|
動作確認
behavior check1 2 | $ curl http: //127 .0.0.1:3000
|
スナップショット確認
ヒープのスナップショット
heap1 2 3 4 5 6 7 | $ curl http: //127 .0.0.1:3000 /snapheap
OK:1409475822884.heapsnapshot
$ ls
1409475822884.heapsnapshot app.js bin node_modules package.json public routes views
|
CPU使用率のスナップショット
heap1 2 3 4 5 6 7 | $ curl http: //127 .0.0.1:3000 /snapcpu
$ ls
1409475822884.heapsnapshot 1409477190566.cpuprofile app.js bin node_modules package.json public routes views
|
Chromeブラウザで結果を確認
CPUとヒープ