zuqqhi2のIT日記

プログラミング + アカデミック + 何か面白いこと

   Aug 31

[node.js][exprees]How to take snapshot of CPU and Heap

by zuqqhi2 at 2014年8月31日
Pocket

Overview

I used to be annoyed by memory leak (and CPU load) when I use node.js for web app development.
I tried to take snapshots and found bottle necks.
This article is how to take snapshot with memory and CPU.
We can check the result of snapshot with Google Chrome browser’s developer tool.

Work Environment

  • 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
    • version: v0.10.26
  • Library

Preparation

Install nvm and node.js here.

Bulding

Make template with express

$ 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

Development sample app

$ 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

var express = require('express');
var router = express.Router();

var request = require('request');
var cheerio = require('cheerio');

/* GET home page. */
router.get('/', function(req, res) {
  var url = 'http://160.16.71.152:12080/';

  request(url, function (error, response, body) {
    // Connection success
    if (!error && response.statusCode == 200) {
      // Parse
      var $ = cheerio.load(body);
      var hrefs = [];

      // Retrieve all link urls in the page
      $('a').each(function() {
        hrefs.push($(this).attr('href'));
      });

      // return response
      var result = {}
      result.status = 'success';
      result.contents = hrefs;
      res.send(JSON.stringify(result));
    // Error result
    } else {
      var result = {}
      result.status = 'error';
      result.contents = '';
      res.send(JSON.stringify(result));
    }
  })
});

module.exports = router;

Development snapshot part

$ 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

var express = require('express');
var router = express.Router();

var fs = require('fs');
var profiler = require('nodegrind');

router.get('/', function(req, res) {
  // filename
  var name =  (new Date().getTime()) + '.cpuprofile';

  // Start to measure
  profiler.startCPU(name);

  // Stop to measure after 5 sec
  setTimeout(function(){
    var cpuProfile = profiler.stopCPU(name, 'cpuprofile');

    //Write profile
    fs.writeFile(name, cpuProfile, function(err){
      if (err) res.send(err);
      else     res.send('OK:' + name);
    });
  }, 5000);
});

module.exports = router;

routes/snapheap.js

var express = require('express');
var router = express.Router();

var fs = require('fs');
var heapdump = require('heapdump');

router.get('/', function(req, res) {
  // filename
  var name = (new Date().getTime()) + '.heapsnapshot';

  // Take a snapshot of heap
  heapdump.writeSnapshot(name, function() {
    res.send('OK:' + name);
  });
});

module.exports = router;

Routing Setting

app.js

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();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
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);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
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
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;

Behavior Check

$ curl http://127.0.0.1:3000
{"status":"success","contents":["http://160.16.71.152:12080/","http://160.16.71.152:12080/?page_id=382",.....................,"http://www.adazing.com/"]}

Check result of snapshot

Take snapshot of heap

$ for i in `seq 5`; do curl 'http://127.0.0.1:3000/'; sleep 1s; done

$ curl http://127.0.0.1:3000/snapheap
OK:1409475822884.heapsnapshot

$ ls
1409475822884.heapsnapshot  app.js  bin  node_modules  package.json  public  routes  views

Take snapshot of CPU

$ for i in `seq 10`; do curl 'http://127.0.0.1:3000'; sleep 1s; done

# Do following command during for loop
$ curl http://127.0.0.1:3000/snapcpu

$ ls
1409475822884.heapsnapshot  1409477190566.cpuprofile  app.js  bin  node_modules  package.json  public  routes  views

Result Check

node-snap-cpu

node-snap-cpu2

node-snap-heap

node-snap-heap2

Related Posts

  • 2013年7月28日 [node.js][express][coffeescript] RESTful API part1 やりたいこと node.jsとexpressとcoffeescriptでRESTfulなAPIを作りたい。 環境準備 Coffee Scriptでモデルを書く リクエストラインをValidateするモデルを作ってみる。 […]
  • 2013年8月14日 [express][socket.io]Check it out that chat demo 参考記事 ※この記事は、ただの上記のリンク先の記事を読んだメモです。 1.レシーバーの記述 サーバに対してlistenするから、app.jsの末尾のあたりに以下のように記述すれば良いみたい。 […]
  • 2013年6月27日 [Javascript][Node.js][express]Connection with mongodb mongodbを使って値の保存、読み出しを使ったプログラムを作成してみる。 まずは expressを使用してひな形を作成する。 次にmongodbとの接続に使用するモデルを作成する。 […]
  • 2013年8月4日 [node.js][express]RESTful API part2 やりたいこと mongoDBとつなげてGETパラメタやDBのデータ内容に応じて出力を変える。 ソース […]
  • 2014年9月13日 [Node.js][Kibana]Try to add reverse proxy function 概要 Elastic Searchクラスタが内部ネットワークからしかアクセスできないようになっていて、 そのためにリバースプロキシ経由でKibanaからElastic […]
  • 2014年9月7日 [sbt]How to use plugin 目的 Scala Sbt でプラグインを使う方法のメモ。 Googleで検索しても門外漢用の記事がなくて苦労したため、メモしてみた。 環境 OSLinux www4322gi 3.2.0-64-generic #97-Ubuntu SMP […]
Pocket

You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.