Nodejs 는 Chrome의 V8 엔진을 기반으로 구축된 platform 입니다. C/C++로 구현 되어 있습니다.


독립적인 platform 이기 때문에 운영체제에 영향을 받지 않습니다. 비슷한 언어로는 Java 가 있는데 JVM(Java Virtual Machine) 이라는 가상 머신이 존재 합니다.


위와 같은 언어는 메모리 관리를 엔진이나 가상 머신 에게 위임 하기 때문에 개발자가 메모리 관리를 하지 않고 비지니스 로직에 집중해서 개발이 가능 합니다.


개발자들이 흔하게 얘기하는 Garbage Collection (GC) 가 메모리 관리를 하는 행위 입니다.


2가지 방법으로 테스트를 해봤습니다. 

약 10분 정도 실행을 했습니다.


자동 GC

Sample Code 

var uuid = require('node-uuid');


function _a() {

  console.log('create _a');

}


function _b() {

  console.log('create _b');

}


while(true) {

  global[uuid.v1()] = new _a();

  global[uuid.v1()] = new _b();

}


메모리 정보

Name:   node

State:  R (running)

Tgid:   4266

Pid:    4266

PPid:   3056

TracerPid:      0

Uid:    1000    1000    1000    1000

Gid:    1000    1000    1000    1000

FDSize: 256

Groups: 4 24 27 30 46 109 124 1000

VmPeak:  2749292 kB

VmSize:  2748676 kB  - (전체 할당된 가상 메모리 크기)

VmLck:         0 kB

VmPin:         0 kB

VmHWM:   1862144 kB

VmRSS:   1827840 kB  - (실제 물리적 메모리 할당된 크기)

VmData:  2721992 kB  - (Heap 영역)

VmStk:       136 kB - (Stack 영역)

VmExe:      8024 kB

VmLib:      4084 kB

VmPTE:      6716 kB

VmSwap:   303584 kB

Threads:        2

SigQ:   0/15889

SigPnd: 0000000000000000

ShdPnd: 0000000000000000

SigBlk: 0000000000000000

SigIgn: 0000000000001000

SigCgt: 0000000188004202

CapInh: 0000000000000000

CapPrm: 0000000000000000

CapEff: 0000000000000000

CapBnd: ffffffffffffffff

Cpus_allowed:   3

Cpus_allowed_list:      0-1

Mems_allowed:   00000000,00000001

Mems_allowed_list:      0

voluntary_ctxt_switches:        5762

nonvoluntary_ctxt_switches:     4740072


수동 GC

Sample Code 

var uuid = require('node-uuid');


function _a() {

  console.log('create _a');

}


function _b() {

  console.log('create _b');

}


while(true) {

  global[uuid.v1()] = new _a();

  global[uuid.v1()] = new _b();

  gc(true);

}


실행 시 --expose-gc 옵션을 줘야 한다.

node --expose-gc test.js


메모리 정보

Name:   node

State:  R (running)

Tgid:   4375

Pid:    4375

PPid:   3056

TracerPid:      0

Uid:    1000    1000    1000    1000

Gid:    1000    1000    1000    1000

FDSize: 256

Groups: 4 24 27 30 46 109 124 1000

VmPeak:   718700 kB

VmSize:   700416 kB  - (전체 할당된 가상 메모리 크기)

VmLck:         0 kB

VmPin:         0 kB

VmHWM:     53492 kB

VmRSS:     53492 kB  - (실제 물리적 메모리 할당된 크기)

VmData:   673732 kB  - (Heap 영역)

VmStk:       136 kB - (Stack 영역)

VmExe:      8024 kB

VmLib:      4084 kB

VmPTE:       288 kB

VmSwap:        0 kB

Threads:        2

SigQ:   0/15889

SigPnd: 0000000000000000

ShdPnd: 0000000000000000

SigBlk: 0000000000000000

SigIgn: 0000000000001000

SigCgt: 0000000188004202

CapInh: 0000000000000000

CapPrm: 0000000000000000

CapEff: 0000000000000000

CapBnd: ffffffffffffffff

Cpus_allowed:   3

Cpus_allowed_list:      0-1

Mems_allowed:   00000000,00000001

Mems_allowed_list:      0

voluntary_ctxt_switches:        68

nonvoluntary_ctxt_switches:     80441


위 2가지 상황의 실제 물리적 메모리 할당 크기 를 비교 해보면 약 34배 정도의 차이를 볼 수 있습니다.


실제로 프로그램의 규모가 커지고 Web Application 이라면 접속자가 많아 질수록 메모리 관리의 중요성은 커질 것이라고 생각합니다. 


※ 참고 사이트

 - http://blog.nerds.kr/

 - http://simonmcmanus.wordpress.com/2013/01/03/forcing-garbage-collection-with-node-js-and-v8/

 - http://faq.hostway.co.kr/Linux_ETC/4258



Express 공식 사이트의 설명 이다.


위 방법대로 사용 할 경우 많은 편의성이 제공 된다.

명시 되지 않은 두번째 인자가 있다.


req.param(name, defaultValue)

defaultValue: 값이 undefined 인 경우 기본 값을 설정 한다.


Ex) req.param('name', 'sample')

위와 같이 사용 한다.


※ 참고 사이트

  - nodejs/express: http://expressjs.com/api.html#req.param



nodejs logging 라이브러리에서 winston이 활발하게 사용 되고 있다. 사용 하다보면 시간이 안맞는 경우가 발생 하기도 한다.


ntp 타임서버 설정을 해서 linux 상에서 time 확인을 해보았다.

2013. 09. 23. (월) 12:02:11 KST

정상적으로 나온다. 하지만 log 에서는 -9시간 전으로 나온다.

2013-09-23T03:02:11.913Z ...

해결 방법

../winston/lib/winston/common.js 파일을 수정 한다.

//
// ### function timestamp ()
// Returns a timestamp string for the current time.
//

exports.timestamp = function () {
  // 수정
};

위 function 을 재 구현하면 시간이 원하는 포맷으로 출력 된다.


※ 참고 사이트

  - nodejs/winston: https://github.com/flatiron/winston


'Nodejs' 카테고리의 다른 글

Nodejs Memory 관리  (0) 2013.11.11
Express req.param 사용 하기  (0) 2013.09.23
Nodejs cluster process 값 공유 하기  (0) 2013.08.29
Nodejs Express logger format 설정 하기  (0) 2013.08.01
Nodejs pg library connection pool 관련  (0) 2013.05.16


Sample Code.

Express와 같이 사용 하였다.

var express = require('express')
  , routes = require('./routes')
  , user = require('./routes/user')
  , http = require('http')
  , path = require('path');

var app = express();

var cluster = require('cluster')
  , numCPUs = require('os').cpus().length;

function setting() {
  app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.set('view engine', 'ejs');
    app.use(express.favicon());
    app.use(express.logger('dev'));
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser('your secret here'));
    app.use(express.session());
    app.use(app.router);
    app.use(express.static(path.join(__dirname, 'public')));
  });

  app.configure('development', function(){
    app.use(express.errorHandler());
  });

  app.get('/', routes.index);
  app.get('/users', user.list);

  http.createServer(app).listen(app.get('port'), function(){
    console.log("Express server listening on port " + app.get('port'));
  });
}

function eachWorker(callback) {
  for (var id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}

// master
if (cluster.isMaster) {
  for (var i=0; i<numCPUs; i++) {
    var worker = cluster.fork();

    worker.on('message', function(message) {
      if (message.cmd == 'broadcast') {
        console.log('send worker');

        setInterval(function() {
          eachWorker(function(_worker) {
            _worker.send({ cmd: 'broadcast', message: 'test message' });
          })
        }, 1000);
      }
    });
  }

  cluster.on('exit', function(worker, code, signal) {
    log.info('worker ' + worker.process.pid + ' died');
  });

// worker
} else {
  if (cluster.isWorker) {
    console.log(' I am worker ' + cluster.worker.id);

    process.on('message', function(message) {
      if (message.cmd == 'broadcast') {
        console.log('receive message: ' + message.message);
      }
    });

    process.send({ cmd: 'broadcast' });
  }
}

1.1  Master (Parent)

부모 프로세스 로 볼 수 있으며 자식 프로세스를 생성 한다. 위 코드를 참고 시 'broadcast' 라는 명령어로 event 감지 하여 데이터를 주고 받는다.


1.2  Worker (Child)

부모 프로세스 가 fork() 할 경우 생성 된다. 해당 프로세스는 생성 되는 시점에서 'message' 라고 명령어를 보낸다.


1.3  Test 

sample를 실행해 보자.


whitelife@whitelife:~/work/cluster_test$ node app.js 

 I am worker #1

send worker.....

 I am worker #2

send worker.....

receive message: test message

receive message: test message


※ 참고 사이트

  - node api: http://www.nodejs.org/api/cluster.html



app.js 파일 중 일부

// ..
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.bodyParser());
// ..

log 파일 중 일부

GET / 200 119ms - 505b

GET /stylesheets/style.css 304 27ms


log를 좀더 자세히 보고 싶은 경우

app.use(express.logger({ format: ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"' }));


format 설정을 위와 같이 넣어 준다.

192.168.0.xx - - [Thu, 01 Aug 2013 01:53:19 GMT] "GET / HTTP/1.1" 200 505 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36"


위와 같이 상세하게 정보를 볼 수 있다.




npm 저장소에 있는 pg library 를 설치 할 경우 아래 경고 메시지가 적용이 된다.


WARNING!!

pg.connect(function(err, client) { ...}) is deprecated and will be removed it v1.0.0 (very soon)

instead, use pg.connect(function(err, client, done) { ... })

automatic releasing of clients back to the pool was a mistake and will be removed

please see the following for more details:

https://github.com/brianc/node-postgres/wiki/pg

https://github.com/brianc/node-postgres/issues/227

https://github.com/brianc/node-postgres/pull/274

feel free to get in touch via github if you have questions


- Connect

기존 방식

pg.connect(function(err, client) { ...});


위의 형태로 connection을 생성 한다면 connection pool에 반환이 되지 않으므로 pool error 가 발생 한다.

connection 자원을 받을 수 없으므로 db와의 통신 불능 상태가 된다.


해결 방법


Result 1.  connect 방식 변경

pg.connect(function(err, client, done) { 

  // ...

  done();

});


done을 call 함으로서 connection pool 에 자원을 반환 한다.


Result 2.  version 0.14.1 이용

0.14.1 을 이용하면 기존 방식으로도 통신이 가능 하다.




Nodejs, npm 은 설치 되어 있다고 가정 한다.


Step 1.  express 설치 하기

-g 옵션을 주게 되면 전역으로 설치 된다.


whitelife@whitelife:~/work$ sudo npm install -g express


Step 2.  프로젝트 생성 하기

생성하기 전에 도움말부터 보자.


whitelife@whitelife:~/work$ express --help


Usage: express [options]


  Options:


    -h, --help          output usage information

    -V, --version       output the version number

    -s, --sessions      add session support

    -e, --ejs           add ejs engine support (defaults to jade)

    -J, --jshtml        add jshtml engine support (defaults to jade)

    -H, --hogan         add hogan.js engine support

    -c, --css <engine>  add stylesheet <engine> support (less|stylus) (defaults to plain css)

    -f, --force         force on non-empty directory


참고해야 하는 옵션은 보통 view, session 추가 설정 부분 이다. 

이제 프로젝트를 생성해 보자.

view engine 은 따로 설정을 하지 않으면 jade로 설정 된다.


whitelife@whitelife:~/work$ express -s -e whitelife


   create : whitelife

   create : whitelife/package.json

   create : whitelife/app.js

   create : whitelife/public

   create : whitelife/public/javascripts

   create : whitelife/public/images

   create : whitelife/public/stylesheets

   create : whitelife/public/stylesheets/style.css

   create : whitelife/views

   create : whitelife/views/index.ejs

   create : whitelife/routes

   create : whitelife/routes/index.js

   create : whitelife/routes/user.js


   install dependencies:

     $ cd whitelife && npm install


   run the app:

     $ node app


whitelife@whitelife:~/work$ 


Step 3.  의존성 모듈 설치 하기

프로젝트에 대한 정의는 package.json을 참고 하자. 생성한 프로젝트 디덱토리를 보면 찾을 수 있다.


whitelife@whitelife:~/work/whitelife$ ll

합계 28

drwxr-xr-x  5 whitelife whitelife 4096  5월  3 11:24 ./

drwxrwxr-x 25 whitelife whitelife 4096  5월  3 11:24 ../

-rw-rw-r--  1 whitelife whitelife  937  5월  3 11:24 app.js

-rw-rw-r--  1 whitelife whitelife  181  5월  3 11:24 package.json

drwxr-xr-x  5 whitelife whitelife 4096  5월  3 11:24 public/

drwxr-xr-x  2 whitelife whitelife 4096  5월  3 11:24 routes/

drwxr-xr-x  2 whitelife whitelife 4096  5월  3 11:24 views/

whitelife@whitelife:~/work/whitelife$ vim package.json 


package.json

{

  "name": "application-name",

  "version": "0.0.1",

  "private": true,

  "scripts": {

    "start": "node app"

  },  

  "dependencies": {

    "express": "3.1.0",

    "ejs": "*" 

  }

}


npm을 이용하여 의존성 모듈을 설치하는데 위 dependencies 속성을 메타 정보로 활용 한다.

java 진영의 소스 관리 툴 maven 과 흡사하다고 생각 하면 된다.

이제 의존성 모듈을 설치 하자.


whitelife@whitelife:~/work/whitelife$ npm install -d

npm info it worked if it ends with ok

npm info using npm@1.2.18

npm info using node@v0.10.5

npm WARN package.json application-name@0.0.1 No README.md file found!

npm info preinstall application-name@0.0.1

npm info trying registry request attempt 1 at 11:32:05

npm http GET https://registry.npmjs.org/ejs

npm info trying registry request attempt 1 at 11:32:05


// ...... ing

ejs@0.8.3 node_modules/ejs

express@3.1.0 node_modules/express
├── methods@0.0.1
├── fresh@0.1.0
├── range-parser@0.0.4
├── cookie-signature@0.0.1
├── buffer-crc32@0.1.1
├── cookie@0.0.5
├── commander@0.6.1
├── debug@0.7.2
├── mkdirp@0.3.3
├── send@0.1.0 (mime@1.2.6)
└── connect@2.7.2 (pause@0.0.1, bytes@0.1.0, formidable@1.0.11, qs@0.5.1)
npm info ok 


설치가 완료되면 의존성 모듈의 구조를 볼 수 있다.

심심할 때 마다 보다보면 좋은 모듈도 많다. mkdirp 를 여기서 보다가 참고해서 사용하고 있다.~ 


따로 모듈을 추가해야 할 경우 --save 옵션을 주면 package.json 에 같이 적용 된다.

whitelife@whitelife:~/work/whitelife$ npm install --save super

whitelife@whitelife:~/work/whitelife$ vim package.json 


// ...

"dependencies": {

  "express": "3.1.0",

  "ejs": "*" 

  "super": "~0.2.1" 

}

// ...


위와 같이 적용 한다면 해당 프로젝트를 다른 PC에 셋팅을 해야 한다고 해도 npm install -d 로 한번에 의존성 모듈 설치가 완료 된다. 


Step 4.  프로젝트 실행 하기

app.js 가 express의 설정 파일이라고 생각 하면 된다. 실행 해보자.


whitelife@whitelife:~/work/whitelife$ node app.js 

Express server listening on port 3000


브라우저를 띄워서 확인 하자.



여기까지 같이 왔다면 성공이다.~ 


※ 참고 사이트: http://expressjs.com/



기본 적으로 설치가 되지 않기 떄문에 추가로 설치 해줘야 한다.


sudo npm install -g node-gyp


+ Recent posts