Skip to main content
박진호
 Node 및 Redis 시작
 게임 순위표 만들기
 메시지 큐 만들기
 Express 애플리케이션에 Stats 미들웨어 추
가
 메모리 내의 키/ 값 저장소로 유명
 Node는 Redis, Memcached,Cassandra 모두
지원
 빠른 액세스를 위해 데이터 쿼리를 메모리
상에 캐시하기 위해 사용
 분산 컴퓨팅에도 적합
 복잡한 데이터에 대해서는 제한된 지원만을
제공
 대량의 쿼리를 처리하는 애플리케이션에는
유용하지만, 데이터를 읽고 쓰는 것이 많은
애플리케이션에서는 유용성이 떨어짐
 http://memcached.org/
 MemCached 처럼 클러스터 지원
 MemCached와 마찬가지로 지원하는 데이
터 구조가 제한
 Redis에 적합하지 않은 임시 쿼리를 처리하
는데 알맞음
 http://cassandra.apache.org/
 데이터를 읽고 쓰는 것이 많은 곳에 사용
 영속적으로 저장 가능
 다양한 유형의 데이터를 지원.
(Memcached에 비해 더 많은 유연성 제공)
 Cassandra에 비해 빠름.
 단일 머신에서만 구동.
 Http://redis.io/
 https://github.com/dmajkic/redis/downloads
 데이터베이스를 이용하면 데이터를 영속적으
로 관리할 수 있지만, 입출력에 다소 시간이 걸
리기 때문에 실시간 서비스에서는 더 적합한
저장소를 사용할 필요
 Redis는 메모리 기반의 저장소이기 때문에 필
요한 정보를 빠르게 저장하고 가져올 수 있는
실시간 서비스에 적합한 저장소
 멤버 id, 플레이어 이름, 게임이름, 마지막 플레
이한 날짜, 점수, 나머지 관련 정보 저장.
 https://github.com/dmajkic/redis/downloads
 Redis를 사용하여 구현.
 npm install redis
 hiredis라는 공식적인 hiredis C 라이브러리를
바인딩하여 Non-Blocking의 빠른 모듈
 npm install hiredis redis
 Async 설치 (series 기능 사용하기 위함. 호출이
순서대로 호출되고 데이터 역시 순서대로 반환
보장)
 npm install async
 Redis 모듈 포함
 Var redis = require(‘redis’);
 Redis 클라이언트 생성
 Var client = redis.createClient();
 3개의 선택적인 파라미터
▪ Port : 6379
▪ Host : 127.0.0.1
▪ 옵션 : parser, return_buffers, detect_buffers, socket_nodelay,
no_ready_check
 연결 종료
 Client.quit();
 강제 종료
 Client.end();
 Redis 모듈 포함
 Var redis = require(‘redis’);
 Redis 클라이언트 생성
 Var client = redis.createClient();
 3개의 선택적인 파라미터
▪ Port : 6379
▪ Host : 127.0.0.1
▪ 옵션 : parser, return_buffers, detect_buffers, socket_nodelay,
no_ready_check
 연결 종료
 Client.quit();
 강제 종료
 Client.end();
 Redis 해시 속성 설정
Client.hset(“hashid”, “propname”, “propvalue”,
function(err, reply)
{
오류 혹은 응답에 대해 무엇인가를 수행
} );
 성공 확인 응답
 Rredis.print
▪ 에러나 응답을 콘솔에 출력 후 반환
 Client.hset (“hashid”, “propname”, “propvalue”,
redis.print);
var net = require('net');
var redis = require('redis');
var server = net.createServer(function(conn) {
console.log('connected');
// create Redis client
var client = redis.createClient();
client.on('error', function(err) {
console.log('Error ' + err);
});
// fifth database is game score database
client.select(5);
conn.on('data', function(data) {
console.log(data + ' from ' + conn.remoteAddress +
' ' +
conn.remotePort);
try {
var obj = JSON.parse(data);
// add or overwrite score
client.hset(obj.member, "first_name",
obj.first_name, redis.print);
client.hset(obj.member, "last_name",
obj.last_name, redis.print);
client.hset(obj.member, "score", obj.score,
redis.print);
client.hset(obj.member, "date", obj.date,
redis.print);
// add to scores for Zowie!
client.zadd("Zowie!", parseInt(obj.score),
obj.member);
} catch(err) {
console.log(err);
}
});
conn.on('close', function() {
console.log('client closed connection');
client.quit();
});
}).listen(8124);
console.log('listening on port 8124');
 var net = require('net');
var client = new net.Socket();
client.setEncoding('utf8');
// connect toTCP server
client.connect
('8124','examples.burningbird.net',
function () {
console.log('connected to server');
});
// prepare for input from terminal
process.stdin.resume();
// when receive data, send to server
process.stdin.on('data', function (data) {
client.write(data);
});
// when receive data back, print to console
client.on('data',function(data) {
console.log(data);
});
// when server closed
client.on('close',function() {
console.log('connection is closed');
});
 두개의 다른 데이터 저장소가 업데이트 됨.
 개별적인 점수 정보(이름, 점수, 날짜를 포
함)은 hash에 저장
 Client.hset ( obj.member, “first_name”,
obj.first_name, redis.print );
 멤버 id 와 점수는 sorted set에 저장
 Client.zadd( “Zowie!”, parseInt(obj.score),
obj.member );
 Score.jade
 Doctype 5  doctype html
 doctype html
html(lang="en")
head
title Zowie!Top Scores
meta(charset="utf-8")
| <style type="text/css">
include main.css
| </style>
body
table
caption Zowie!Top Scorers!
tr
th Score
th Name
th Date
if scores.length
each score in scores
if score
tr
td #{score.score}
td #{score.first_name} #{score.last_name}
td #{score.date}
 body {
margin: 50px;
}
table {
width: 90%;
border-collapse: collapse;
}
table,
td,
th,
caption {
border: 1px solid #000;
}
td {
padding: 20px;
}
 caption {
font-size: larger;
background-color: #ff0;
padding: 10px;
}
h1 {
font: 1.5em Georgia, serif;
}
ul {
list-style-type: none;
}
form {
margin: 20px;
padding: 20px;
}
 var http = require('http');
var async = require('async');
var redis = require('redis');
var jade = require('jade');
// set up Jade template
var layout =
require('fs').readFileSync(__dirname +
'/score.jade', 'utf8');
var fn = jade.compile(layout, {filename:
__dirname + '/score.jade'});
// start Redis client
var client = redis.createClient();
// select fifth database
client.select(5);
// helper function
function makeCallbackFunc(member) {
return function(callback) {
client.hgetall(member, function(err, obj) {
callback(err,obj);
});
};
}
 http.createServer(function(req,res) {
// first filter out icon request
if (req.url === '/favicon.ico') {
res.writeHead(200, {'Content-Type': 'image/x-
icon'} );
res.end();
return;
}
// get scores, reverse order, top five only
client.zrevrange('Zowie!',0,4, function(err,result) {
var scores;
if (err) {
console.log(err);
res.end('Top scores not currently available,
please check back');
return;
}
 // create array of callback functions for
Async.series call
var callFunctions = new Array();
// process results with
makeCallbackFunc, push newly returned
// callback into array
for (var i = 0; i < result.length; i++) {
callFunctions.push(makeCallbackFu
nc(result[i]));
}
 // using Async series to process each callback in
turn and return
// end result as array of objects
async.series(
callFunctions,
function (err, result) {
if (err) {
console.log(err);
res.end('Scores not available');
return;
}
// pass object array to template engine
var str = fn({scores : result});
res.end(str);
});
});
}).listen(3000);
console.log('Server running on 3000/');
 메시지 큐
 특정한 통신 형식을 입력으로 받아 큐에 저장하
는 애플리케이션
 메시지는 메시지 수신자가 가져갈 때 까지 저장
되었다가 해당 시점에 큐에서 뽑혀져서 수신자
에게 전송(한번에 하나씩 혹은 대량))
 통신은 비동기로 이루어짐
 메시지 큐를 보여주기 위해 여러 개의 다양한 하위
도메인에 대한 웹 로그 파일에 접근하는 애플리케이
션
 메시지 큐 애플리케이션이 수행하는 것은 3000번에
서 메시지를 수신대기하다가 전송된 항목을 Redis
데이터 저장소로 저장
 로그 항목을 받은 후 로그 데이터에서 이미지 리소
스(jpg, gif 등)가 접근되었는지를 찾아보는 정규식
검사를 수행. 일치하는 패턴이 발견되면 리소스 URL
을 메시지 큐 애플리케이션에 전송
 Redis 클라이언트를 만들어서 애플리케이션이 종료될
때 까지 지속?
 Redis 클라이언트를 만든 후 Redis 명령어를 실행하자 마
자 바로 해제?
 영구적이 좋은가? 즉시 해제하는 것이 좋은가?
 클라이언트 연결유지가 더 빠를것이라는 예상 맞음. 연결을
유지하는 경우를 테스트 하는 도중 애플리케이션이 잠시 동
안 상당히 느려졌다가 상대적으로 빠른 속도를 회복
 Redis 데이터베이스에 대해 대기 중인 요청들이 큐가 해제도
리 때 까지 Node 애플리케이션을 일시적으로 차단시켰기 때
문. 매번 요청할 때마다 연결을 열고 닫는 경우에는 동일한 상
황을 겪지 않았는데, 열고 닫는 과정에 들어가는 추가 오버헤
드가 애플리케이션의 성능을 저하시켜서 동시 사용자 상한선
에 도달하지 않았기 때문
 이전 장들에서 만들어본 위젯 애플리케이션
에 통계를 추가하기 위해 Redis를 사용
 통계는 위젯 애플리케이션의 페이지에 접근
하는 모든 ip 주소들의 집합과 각 리소스가
접근된 횟수라는 두개로 제한
 두 개의 분리된 데이터 컬렉션을 한번에 가
져오기 위해 redis 트랜잭션을 제어하는
multi 사용
 1. Redis 데이터 베이스에 접근 정보를 기록
하기 위한 새로운 미들웨어 추가
 Ip 주소를 추가
▪ Client.sadd(‘ip’, req.socket.remoteAddress);
 리소스 카운트 증가
▪ Client.hincrby(‘myurls’, req.url, 1);
 2. routes/index.js
 통계 애플리케이션에서 새로운 컨트롤러 코드를 가진 라우팅 색인 파일
 통계 인터페이스는 최상위 도메인에서 접근되므로 routes 폴더에 추가.
 Express 4에서 에러나는 부분들…. 좀 더 찾아봐야…
 app.use(express.favicon()); app.use(express.logger('dev'));
app.use(express.staticCache({maxObjects: 100, maxLength: 512}));
 app.use(express.bodyParser());
 app.use(express.methodOverride());
 app.use(app.router);
 app.use(express.directory(__dirname + '/public'));
 app.use(function(req, res, next){ throw new Error(req.url + ' not found'); });
app.use(function(err, req, res, next) { console.log(err);
res.send(err.message); });
감사합니다