Продолжаем рассматривать варианты хранения данных приложения Node.js на примере создания простого чата на TCP-сокетах. На очереди хранилище Redis. Предлагаю хранить сообщения нашего чата в структуре данных по имени список. Списки в Redis позволяют хранить и манипулировать массивами значений для заданного ключа - то, что нужно для нашего чата.
Для начала в каталоге приложения создадим каталог node_modules, в который будем устанавливать модули, необходимые для работы нашего приложения.
Затем создадим файл package.json с помощью утилиты npm init:
После того, как мы согласимся с содержимым файла, предложенным утилитой npm init, файл package.json будет создан:
Теперь можно установить модуль redis, а также одновременно сохранить информацию об установленном модуле в раздел dependencies файла package.json, выполнив в консоли команду npm install --save redis:
Редактируем код нашей модели данных - файла message.js.
Единственный модуль, который нам понадобится для работы с данными - это разумеется redis.
Ограничим размер списка - структуры данных, в которой мы будем хранить наши сообщения - следующим образом: сразу же после сохранения сообщения с помощью команды lpush предлагаю обрезать размер списка командой ltrim, создав тем самым очередь сообщений ограниченного размера:
На текущем этапе изменения в файле server.js незначительны - я немного изменил код получения сообщений сразу после присоединения к чату нового пользователя - привел его в соответствие экспортируемому методу get модели данных:
Не забываем скачать бинарники Redis, запускаем redis-server:
Запускаем северную часть чата, пару клиентов, начинаем разговор:
Запускаем третьего клиента...
... и замечаем, что сообщения у нас появляются в обратном порядке :).
Мой косяк, отредактируем код файла message.js - перевернем массив, который возвращает метод get модели данных.
Кроме того, раз уж все равно полезли в код модели, предлагаю немного изменить код функции обратного вызова того же метода get с тем, чтобы она соответствовала соглашению "error first":
Соответственно подрихтуем и код сервера:
Запускаем сервер, пару клиентов:
С Redis все. В следующей серии рассмотрим вариант с использованием MongoDB Native Driver. Продолжение следует...
Для начала в каталоге приложения создадим каталог node_modules, в который будем устанавливать модули, необходимые для работы нашего приложения.
Затем создадим файл package.json с помощью утилиты npm init:
После того, как мы согласимся с содержимым файла, предложенным утилитой npm init, файл package.json будет создан:
Теперь можно установить модуль redis, а также одновременно сохранить информацию об установленном модуле в раздел dependencies файла package.json, выполнив в консоли команду npm install --save redis:
Редактируем код нашей модели данных - файла message.js.
Единственный модуль, который нам понадобится для работы с данными - это разумеется redis.
Ограничим размер списка - структуры данных, в которой мы будем хранить наши сообщения - следующим образом: сразу же после сохранения сообщения с помощью команды lpush предлагаю обрезать размер списка командой ltrim, создав тем самым очередь сообщений ограниченного размера:
var redis = require("redis"); module.exports = function(len, size) { this.len = len || 100; // длина сообщения по умолчанию - 100 символов this.size = size || 19; //размер кэша сообщений по умолчанию - 20 штук this.insert = function(client, data, cb) { if(data.length > this.len) { cb('Too much information!'); return; } data = {data: data, from: client['_id'], ts: Date.now()}; var client = createRedisClient(); client.lpush('list:msg', JSON.stringify(data), redis.print); client.ltrim('list:msg', 0, this.size, redis.print); client.quit(); cb(false, createMessage(data)); } this.get = function(n, cb) { var i = n || 5; // количество сообщений по умолчанию - 5 штук var client = createRedisClient(); client.lrange('list:msg', 0, i, function(err, list) { cb(list.map(function(msg) { return createMessage(JSON.parse(msg)); }).join('\n')); client.quit(); }); } } function createMessage(msg) { return new Date(msg.ts).toLocaleTimeString() + ' ' + msg.from + ' >>> ' + msg.data; } function createRedisClient() { var client = redis.createClient(); client.on("error", function (err) { console.log("--- redis client error ---\n%s", err); }); return client; }
На текущем этапе изменения в файле server.js незначительны - я немного изменил код получения сообщений сразу после присоединения к чату нового пользователя - привел его в соответствие экспортируемому методу get модели данных:
var net = require('net'); var PubSub = require('./pubsub'), pubsub = new PubSub; var Message = require('./message'), message = new Message(); var server = net.createServer(function(socket) { socket.setEncoding('utf8'); console.log('--- socket connected ---\nfrom: %s', socket.remoteAddress + ':' + socket.remotePort); pubsub.emit('join', socket, function() { message.get(null, function(data) { socket.write('\n' + data); }); }); socket.on('data', function(data) { data = data.replace(/\r\n$/, ''); console.log('--- socket data ---\n%s', data); message.insert(this, data, function(err, data) { if(err) { socket.write(err); return; } pubsub.emit('broadcast', socket, data); }); }); socket.on('close', function() { console.log('--- socket closed ---'); pubsub.emit('leave', this); }); socket.on('end', function() { console.log('--- socket end ---'); pubsub.emit('leave', this); }); socket.on('error', function(e) { console.log('--- server error ---\ncode: %s', e.code); }); }); server.listen(8124, function() { console.log('Chamber of Secrets is opened on port %d...', this.address()['port']); });
Не забываем скачать бинарники Redis, запускаем redis-server:
Запускаем северную часть чата, пару клиентов, начинаем разговор:
Запускаем третьего клиента...
... и замечаем, что сообщения у нас появляются в обратном порядке :).
Мой косяк, отредактируем код файла message.js - перевернем массив, который возвращает метод get модели данных.
Кроме того, раз уж все равно полезли в код модели, предлагаю немного изменить код функции обратного вызова того же метода get с тем, чтобы она соответствовала соглашению "error first":
var redis = require("redis"); module.exports = function(len, size) { this.len = len || 100; // длина сообщения по умолчанию - 100 символов this.size = size || 19; //размер кэша сообщений по умолчанию - 20 штук this.insert = function(client, data, cb) { if(data.length > this.len) { cb('Too much information!'); return; } data = {data: data, from: client['_id'], ts: Date.now()}; var client = createRedisClient(); client.lpush('list:msg', JSON.stringify(data), redis.print); client.ltrim('list:msg', 0, this.size, redis.print); client.quit(); cb(false, createMessage(data)); } this.get = function(n, cb) { var i = n || 5; // количество сообщений по умолчанию - 5 штук var client = createRedisClient(); client.lrange('list:msg', 0, i, function(err, list) { if(err) return cb('--- error getting data ---'); cb(false, list.map(function(msg) { return createMessage(JSON.parse(msg)); }).reverse().join('\n')); client.quit(); }); } } function createMessage(msg) { return new Date(msg.ts).toLocaleTimeString() + ' ' + msg.from + ' >>> ' + msg.data; } function createRedisClient() { var client = redis.createClient(); client.on("error", function (err) { console.log("--- redis client error ---\n%s", err); }); return client; }
Соответственно подрихтуем и код сервера:
var net = require('net'); var PubSub = require('./pubsub'), pubsub = new PubSub; var Message = require('./message'), message = new Message(); var server = net.createServer(function(socket) { socket.setEncoding('utf8'); console.log('--- socket connected ---\nfrom: %s', socket.remoteAddress + ':' + socket.remotePort); pubsub.emit('join', socket, function() { message.get(null, function(err, data) { if(err) return socket.write(err); socket.write('\n' + data); }); }); socket.on('data', function(data) { data = data.replace(/\r\n$/, ''); console.log('--- socket data ---\n%s', data); message.insert(this, data, function(err, data) { if(err) return socket.write(err); pubsub.emit('broadcast', socket, data); }); }); socket.on('close', function() { console.log('--- socket closed ---'); pubsub.emit('leave', this); }); socket.on('end', function() { console.log('--- socket end ---'); pubsub.emit('leave', this); }); socket.on('error', function(e) { console.log('--- server error ---\ncode: %s', e.code); }); }); server.listen(8124, function() { console.log('Chamber of Secrets is opened on port %d...', this.address()['port']); });
Запускаем сервер, пару клиентов:
С Redis все. В следующей серии рассмотрим вариант с использованием MongoDB Native Driver. Продолжение следует...
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации