Rev. 2.73

express.png

Node.JS용 MVC 프레임워크인 Express를 사용해 보면서, 쓰면 쓸수록 잘 만들었다는 생각이 들어 학습에 목적을 둔 날번역을 주말내내 진행했습니다. 생각보다 양이 많지 않아 약 50% 정도 진행되었으며, 주중으로 마무리 지을 예정입니다. 그런데 문서를 들여다 보면 볼수록 더욱 잘 만들어 졌었다는 생각을 지울수가 없네요. 지금까지의 놀라움을 요약해 보면, 정규식을 지원하는 강력한 라우팅 메커니즘미들웨어 개념을 도입한 말도 안되게 쉬운 기능확장, 심플한 라우트 필터링, 폼전송시 PUT 및 DELETE 메서드의 고질적인 문제를 methodOverride 미들웨어로 허무하게 해결, 응답형태가 json이면 json으로 오류를 뱉어 주시는 센스, 다중으로 템플릿 엔진을 허용하는 초간단 변수 전달 뷰 시스템까지... 아주 쩔어요. 라우트 자체에 미들웨어나 조건들를 끼워넣고 순서대로 순환해 가며 처리하는 아이디어가 Express의 핵심이라 볼 수 있겠네요. 아~ 이제 서버-사이드도 할만해 졌어... 서버-사이드로 넘어갈까봐요.

* 2011-08-23, 1차 번역 완료 했습니다. 문맥상의 오류는 차차 잡아 가겠습니다. 혹시모를 오역이나 추가의견이 있으시면 좀 알려주세요;

Comments

얼마전 LearnBoost에서 개발한 Cluster라는 Node.JS용 모듈을 알게되었습니다. 이 모듈은 노드 애플리케이션을 멀티-코어로 구동시켜 줍니다. 마스터, 워커로 간단하게 구성된 계층구조를 가지며 워커는 하나의 물리적인 코어를 가지게 되고 마스터만 죽이면 워커는 알아서 죽습니다. 또한 다양한 추가 기능을 제공하는 플러그인들이 있습니다. 특히, repl이라는 플러그인을 로드하면 telnet으로 접속할 수 있는 관리환경을 제공하게 되는데, 워커의 상황을 감시하고 즉시 죽이거나 스폰할 수있습니다. 기본적인 설치 및 사용방법은 다음과 같습니다.

# npm install cluster
# touch server.js
# vim server.js
var http = require('http');

var server = http.createServer(function(req, res){
  res.writeHead(200);
  res.end('Hello World');
});

// server start listening
if (module === require.main) server.listen(3000);
else module.exports  = server;

console.log('listen on port 3000');
# touch cluster.js
# vim cluster.js
var cluster = require('cluster');

cluster('./server')
  .use(cluster.repl('/var/run/cluster.sock'))
  .listen(3000);
# node cluster.js
listen on port 3000
listen on port 3000
listen on port 3000
listen on port 3000
listen on port 3000
listen on port 3000
listen on port 3000
listen on port 3000

# telnet /var/run/cluster.sock
cluster> help()

  Commands
  help(): Display help information
  spawn(n): Spawn one or more additional workers
  pids(): Output process ids
  kill(id, signal): Send signal or SIGTERM to the given worker
  shutdown(): Gracefully shutdown server
  stop(): Hard shutdown
  restart(): Gracefully restart all workers
  echo(msg): echo the given message
  stats(): Display server statistics

cluster> pids()
  master: 25930
  worker #0: 25933
  worker #1: 25934
  worker #2: 25935
  worker #3: 25936
  worker #4: 25937
  worker #5: 25938
  worker #6: 25939
  worker #7: 25940

제가 사용하는 가상서버의 코어는 8개입니다. 기본으로 하나의 코어에 하나의 워커를 배정하게 됩니다. 이제 포트 3000번으로 접속하는 서버 프로그램은 코어 래밸에서 클러스터링 된 것입니다. 참 쉽죠? 보다 자세한 사용방법은 api 문서를 숙지하세요.

epxpress 프레임웍으로 간단하게 만들어본 Firejune.I/O의 인덱스에 적용시키고 약 이틀정도 시간이 경과했습니다. 아주 잘돌아 가고 있어요. 주의해야 할 점은 서로다른 코어에서 작동하기 때문에 어떠한 변수를 참조할 수 없는 경우가 발생합니다. 그래서 이 변수를 워커들이 공유해야한다면 Redis와 같은 데이터스토어를 이용하여 해결해야합니다. MemoryStore를 기본으로 사용하는 Socket.IO역시 이러한 문제를 안고 있습니다. 접속이 성공적으로 이루어졌지만 상대방에게 메시지를 전송하지 못하는 상태인거죠. 그들은 사용할 Store를 옵션화하고 자체적으로 RedisStore를 제공하여 이 문제를 해결할 계획인듯 합니다.

var express= require('express')
  , sio = require('socket.io');

var app = express.createServer();
var io = sio.listen(app);
io.configure(function () {
  io.set('store', new sio.RedisStore);
});

if (module === require.main) app.listen(3000);
else module.exports  = app;

그러나 Socket.IO 최신버전(0.7.9)에서는 './lib/stores/redis.js'에서 'removeEvent'라는 속성이 없다는 오류가 발생합니다. 이 문제를 Daniel Shaw씨가 수정(그리고 이것)하여 GitHub에 풀(pull)된 상태이며 0.8 버전에서 적용될 것이라고 하네요. 이 소스를 반영하면 오류는 사라지지는데 뭔가(메모리가 누수되고 있다는;) 경고가 계속 나옵니다. 하지만 작동에는 이상이 없더군요. 송/수신 패킷이 상상을 초월하는 웹소켓에 적용하면 금상첨화 아니겠어요? 0.8이 나오기를 기다려 봐야겠습니다.

참고로, LampWebDevelopers.com에서도 자신들의 서비스에 Cluster 모듈을 적용하여 서버의 물리적 변화를 관측한 포스팅을 했습니다.

Comments

지난번에 소개한 Jason Davies씨의 websocket-activity-monitor 소스 리파지토리를 포크해서 재구성해보았습니다. 이것은 실시간으로 서버의 활성상태를 모니터링하는 프로젝트입니다. iostate에서 흘려주는 정보들을 조금더 출력할 수 있게하고 차트 라이브러리를 Dygraph로 변경하여 약 10여분 동안의 데이터가 누적되도록 하였습니다. 그 결과는 아래와 같습니다. 당분간 Firejune.I/O의 인덱스 페이지로 사용할 예정이에요.

firejune.io.png

avg-cpu
%user : 사용자 레벨(응용프로그램)에서 실행하는 동안 발생한 CPU 사용률
%nice : 우선순위를 가진 사용자 수준에서 실행하는 동안 발생한 CPU 사용률
%system : 시스템 레벨(커널)에서 실행하는 동안 발생한 CPU 사용률
%iowait : 스템이 미결 디스크 입/출력 요청이 있었던 동안 CPU가 유휴 상태였던 시간의 비율
%steal : 0.10 하이퍼바이저가 다른 가상 프로세서를 서비스하는 동안 CPU가 유휴 상태였던 시간의 비율
%idle : CPU가 유휴 상태였으며 시스템에 미결 디스크 입/출력 요청이 없었던 시간의 퍼센트

device I/O
rram/s : 장치에 병합된 초당 읽기 요청 수
wram/s : 장치에 병합된 초당 쓰기 요청 수
tps : 장치에 발행된 초당 입/출력 수(r/s + w/s)
rsec/s : 장치의 초당 읽은 섹터 수
wsec/s : 장치의 초당 기록한 섹터 수

device average
avgrq-sz : 장치에 발행된 읽기 요청의 평균 크기(섹터)
avgqu-sz : 장치에 발행된 요청 대기열의 평균 길이
await : 장치에 발행된 입/출력 요청이 있었던 동안 CPU가 유휴 상태였던 시간의 평균값(밀리초)
svctm : 장치에 발행된 입/출력 요청에 대한 평균 서비스 시간(밀리초)
%util : 장치의 입/출력 요청에 대한 CPU 대역폭 사용률(100 퍼센트에 가까울 경우 장치가 포화 상태임)

Update:
- 2011/09/10: 러닝 프로세스의 메모리 및 CPU 사용량에 따른 문자 색상 변화
- 2011/09/06: 상호작용 가능한 갱신 시간 및 히스토리 스택 옵션
- 2011/09/05: 데이터 송/수신 논리 변경
- 2011/09/04: 러닝 프로세스 출력 개선
- 2011/09/04: 히스토리 스택 축소
- 2011/09/02: top 명령을 이용한 러닝 프로세스 출력
- 2011/08/30: node-websocket-server 모듈에서 Socket.IO 모듈로 변경
- 2011/08/28: 서버 메모리 스토어에 약 6여분(최근 400건)의 데이터 보존 및 출력
- 2011/08/27: 서버 커낵션 수에 비례하는 콜백 증식 현상(메모리 릭) 제거
- 2011/08/26: 서버와 접속이 끝어진 경우 재접속 시도
- 2011/08/24: free 명령을 이용한 메모리 사용량 데이터 추가
- 2011/08/23: 그래프 생성 및 데이터 가공 분기
- 2011/08/20: 수신 데이터 포멧 변경

Comments