Redisを用いて複数サーバー構成でのリアルタイムチャットアプリを作ってみた
作ったもの
今回はRedisのお話です。
RedisのPubSubを用いることで、複数台サーバー構成のシステムでもリアルタイムチャットを実現可能にしました!
(左は3000番ポート、右は3001番ポートと別々のサーバへ接続しています)
背景
先日CyberAgentさん主催のヒダッカソンに参加してきました!
ハッカソンというよりはISUCONに近いイベントだったのですが、アプリケーション/データベース/インフラと様々な領域での知識と技術が求められ、自分自身の能力不足を痛感しました…
とはいえ凹んでいても周りのエンジニアとの差は深まる一方なので、悔しさをバネに最近はLinuxコマンドやDB周りの知識を増やすべく本やWebページを読みながら勉強しています!
Redisとは
RedisはREmote DIctionary Serviceの略で、Salvatore Sanfilippoさんによって作られたキーバリューストア。
インメモリDBのひとつでメモリ上にデータを保存するため、CPUから直接アクセスすることができ、読み取り/書き込みが高速に行えます。秒間10万SET以上の操作を処理できるだとか…凄すぎ。
PubSubとは
PubSubとはPublish/Subscribeの略で、出版-購読型モデルを意味します。
PubSubにおいて必要になるアクションは2つです。
- Subscriber(購読者)が事前にどのチャネルをSubscribeしておくか設定する。
- Publisher(出版者)がチャネルにメッセージを投げる。
Publisherが使用するチャネル=Subscriberが購読しているチャネル、であればPublisherがメッセージを投げた際にSubscriberは即座にイベントを受け取ることが可能になります。
実装
server.js
const app = require('express')()
const server = require('http').createServer(app)
const io = require('socket.io')(server)
const redis = require('redis')
const sub = redis.createClient()
const pub = sub.duplicate()
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/index.html`)
})
io.on('connection', socket => {
socket.on('chat', msg => {
pub.publish('chat', msg)
})
})
sub.on('message', (channel, message) => {
io.emit('chat', message)
})
sub.subscribe('chat')
server.listen(3000, () => {
console.log('listening on localhost:3000')
})
secondServer.js
const app = require('express')()
const server = require('http').createServer(app)
const io = require('socket.io')(server)
const redis = require('redis')
const sub = redis.createClient()
const pub = sub.duplicate()
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/index.html`)
})
io.on('connection', socket => {
socket.on('chat', msg => {
pub.publish('chat', msg)
})
})
sub.on('message', (channel, message) => {
io.emit('chat', message)
})
sub.subscribe('chat')
server.listen(3001, () => {
console.log('listening on localhost:3001')
})
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
#messages { margin-bottom: 40px }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(function () {
var socket = io();
$('form').submit(function(){
socket.emit('chat', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat', function(msg){
$('#messages').append($('<li>').text(msg));
window.scrollTo(0, document.body.scrollHeight);
});
});
</script>
</body>
</html>
$ node server.js
$ node secondServer.js
苦戦したところ
const app = require('express')()
const server = require('http').createServer(app)
const io = require('socket.io')(server)
const redis = require('redis')
const sub = redis.createClient()
const pub = sub.duplicate()
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/index.html`)
})
io.on('connection', socket => {
socket.on('chat', msg => {
pub.publish('chat', msg)
})
})
sub.on('message', (channel, message) => {
io.emit('chat', message)
})
sub.subscribe('chat')
server.listen(3000, () => {
console.log('listening on localhost:3000')
})
const app = require('express')()
const server = require('http').createServer(app)
const io = require('socket.io')(server)
const redis = require('redis')
const sub = redis.createClient()
const pub = sub.duplicate()
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/index.html`)
})
io.on('connection', socket => {
socket.on('chat', msg => {
pub.publish('chat', msg)
})
})
sub.on('message', (channel, message) => {
io.emit('chat', message)
})
sub.subscribe('chat')
server.listen(3001, () => {
console.log('listening on localhost:3001')
})
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
#messages { margin-bottom: 40px }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(function () {
var socket = io();
$('form').submit(function(){
socket.emit('chat', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat', function(msg){
$('#messages').append($('<li>').text(msg));
window.scrollTo(0, document.body.scrollHeight);
});
});
</script>
</body>
</html>
$ node server.js
$ node secondServer.js
途中メッセージを投げてもサーバー側で一切イベントを受け取ってくれない、という壁にぶつかりました。
1時間ほど悩んでいたところで、サーバ側とフロント側で使用しているSocket.ioのバージョンが違うことに気づきました…失態!
関連記事
Author And Source
この問題について(Redisを用いて複数サーバー構成でのリアルタイムチャットアプリを作ってみた), 我々は、より多くの情報をここで見つけました https://qiita.com/kitakitsune/items/c007529f57ff12e04965著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .