Node Js와 Redis를 사용한 구조화된 데이터

  • View
    2.164

  • Download
    1

  • Category

    Software

Preview:

DESCRIPTION

Node Js와 Redis를 사용한 구조화된 데이터

Citation preview

제대로 배우는 Node Js 9 장 Node 와 Redis 를 사용한 구조화된 데이터

박진호

목차

Node 및 Redis 시작 게임 순위표 만들기 메시지 큐 만들기 Express 애플리케이션에 Stats 미들웨어

추가

Redis, Memcached, Cassan-dra

메모리 내의 키 / 값 저장소로 유명 Node 는 Redis, Memcached, Cassan-

dra 모두 지원

Memcached

빠른 액세스를 위해 데이터 쿼리를 메모리 상에 캐시하기 위해 사용

분산 컴퓨팅에도 적합 복잡한 데이터에 대해서는 제한된 지원만을

제공 대량의 쿼리를 처리하는 애플리케이션에는

유용하지만 , 데이터를 읽고 쓰는 것이 많은 애플리케이션에서는 유용성이 떨어짐

http://memcached.org/

Cassandra

MemCached 처럼 클러스터 지원 MemCached 와 마찬가지로 지원하는

데이터 구조가 제한 Redis 에 적합하지 않은 임시 쿼리를

처리하는데 알맞음 http://cassandra.apache.org/

Redis

데이터를 읽고 쓰는 것이 많은 곳에 사용 영속적으로 저장 가능 다양한 유형의 데이터를 지원 .

(Memcached 에 비해 더 많은 유연성 제공 )

Cassandra 에 비해 빠름 .

단일 머신에서만 구동 . Http://redis.io/

Redis 클라이언트 접속

게임 순위표 만들기

데이터베이스를 이용하면 데이터를 영속적으로 관리할 수 있지만 , 입출력에 다소 시간이 걸리기 때문에 실시간 서비스에서는 더 적합한 저장소를 사용할 필요

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 모듈

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 동작

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.re-moteAddress + ' ' +        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.s-core, 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 to TCP serverclient.connect ('8124','examples.burningbird.net', function () {    console.log('connected to server');});

// prepare for input from terminalprocess.stdin.resume();

// when receive data, send to serverprocess.stdin.on('data', function (data) {   client.write(data);});

// when receive data back, print to consoleclient.on('data',function(data) {    console.log(data);});

// when server closedclient.on('close',function() {    console.log('connection is closed');}); 

게임 순위표 만들기 – Redis 동작

두개의 다른 데이터 저장소가 업데이트 됨 .

개별적인 점수 정보 ( 이름 , 점수 , 날짜를 포함 ) 은 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

Score.jade Doctype 5 doctype html

게임 순위표 만들기 – 상위득점 서버

게임 순위표 만들기 – Score.jade

doctype htmlhtml(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}

게임 순위표 만들기 – main.css

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;}

게임 순위표 만들기 – topScore-Server9-3 var http = require('http');

var async = require('async');var redis = require('redis');var jade = require('jade');

// set up Jade templatevar layout = require('fs').readFileSync(__dirname + '/score.jade', 'utf8');var fn = jade.compile(layout, {file-name: __dirname + '/score.jade'});

// start Redis clientvar client = redis.createClient();

// select fifth databaseclient.select(5);

// helper functionfunction 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;      }

     

게임 순위표 만들기 – topScore-Server9-3 // create array of callback func-

tions for Async.series call      var callFunctions = new Ar-ray();

      // process results with make-CallbackFunc, push newly re-turned      // callback into array      for (var i = 0; i < re-sult.length; i++) {         callFunctions.push(makeCallbackFunc(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 en-gine            var str = fn({scores : result});            res.end(str);       });   });}).listen(3000);

console.log('Server running on 3000/');

메시지 큐 만들기

메시지 큐 특정한 통신 형식을 입력으로 받아 큐에 저장하는

애플리케이션

메시지는 메시지 수신자가 가져갈 때 까지 저장되었다가 해당 시점에 큐에서 뽑혀져서 수신자에게 전송 ( 한번에 하나씩 혹은 대량 ))

통신은 비동기로 이루어짐

메시지 큐 만들기

메시지 큐를 보여주기 위해 여러 개의 다양한 하위 도메인에 대한 웹 로그 파일에 접근하는 애플리케이션

메시지 큐 애플리케이션이 수행하는 것은 3000번에서 메시지를 수신대기하다가 전송된 항목을 Re-dis 데이터 저장소로 저장

로그 항목을 받은 후 로그 데이터에서 이미지 리소스(jpg, gif 등 ) 가 접근되었는지를 찾아보는 정규식 검사를 수행 . 일치하는 패턴이 발견되면 리소스 URL을 메시지 큐 애플리케이션에 전송

Redis 클라이언트 생성 시점 Redis 클라이언트를 만들어서 애플리케이션이 종료될 때 까지

지속 ? Redis 클라이언트를 만든 후 Redis 명령어를 실행하자 마자

바로 해제 ?

영구적이 좋은가 ? 즉시 해제하는 것이 좋은가 ? 클라이언트 연결유지가 더 빠를것이라는 예상 맞음 . 연결을 유지하는

경우를 테스트 하는 도중 애플리케이션이 잠시 동안 상당히 느려졌다가 상대적으로 빠른 속도를 회복

Redis 데이터베이스에 대해 대기 중인 요청들이 큐가 해제도리 때 까지 Node 애플리케이션을 일시적으로 차단시켰기 때문 . 매번 요청할 때마다 연결을 열고 닫는 경우에는 동일한 상황을 겪지 않았는데 , 열고 닫는 과정에 들어가는 추가 오버헤드가 애플리케이션의 성능을 저하시켜서 동시 사용자 상한선에 도달하지 않았기 때문

Express 애플리케이션에 Stats 미들웨어 추가 이전 장들에서 만들어본 위젯 애플리케이션에

통계를 추가하기 위해 Redis 를 사용

통계는 위젯 애플리케이션의 페이지에 접근하는 모든 ip 주소들의 집합과 각 리소스가 접근된 횟수라는 두개로 제한

두 개의 분리된 데이터 컬렉션을 한번에 가져오기 위해 redis 트랜잭션을 제어하는 multi 사용

Express 애플리케이션에 Stats 미들웨어 추가 1. Redis 데이터 베이스에 접근 정보를

기록하기 위한 새로운 미들웨어 추가 Ip 주소를 추가▪ Client.sadd(‘ip’, req.socket.remoteAddress);

리소스 카운트 증가▪ Client.hincrby(‘myurls’, req.url, 1);

Express 애플리케이션에 Stats 미들웨어 추가 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); });

감사합니다

Recommended