Sunting: Di Socket.IO 1.0+, daripada mengatur penyimpanan dengan beberapa klien Redis, modul adaptor Redis yang lebih sederhana sekarang dapat digunakan.
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
Contoh yang ditunjukkan di bawah ini akan terlihat lebih seperti ini:
var cluster = require('cluster');
var os = require('os');
if (cluster.isMaster) {
// we create a HTTP server, but we do not use listen
// that way, we have a socket.io server that doesn't accept connections
var server = require('http').createServer();
var io = require('socket.io').listen(server);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
setInterval(function() {
// all workers will receive this in Redis, and emit
io.emit('data', 'payload');
}, 1000);
for (var i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
}
if (cluster.isWorker) {
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
io.on('connection', function(socket) {
socket.emit('data', 'connected to worker: ' + cluster.worker.id);
});
app.listen(80);
}
Jika Anda memiliki master node yang perlu dipublikasikan ke proses Socket.IO lainnya, tetapi tidak menerima koneksi socket itu sendiri, gunakan socket.io-emitter alih-alih socket.io-redis.
Jika Anda mengalami masalah penskalaan, jalankan aplikasi Node Anda dengan DEBUG=*
. Socket.IO sekarang mengimplementasikan debug yang juga akan mencetak pesan debug adaptor Redis. Contoh keluaran:
socket.io:server initializing namespace / +0ms
socket.io:server creating engine.io instance with opts {"path":"/socket.io"} +2ms
socket.io:server attaching client serving req handler +2ms
socket.io-parser encoding packet {"type":2,"data":["event","payload"],"nsp":"/"} +0ms
socket.io-parser encoded {"type":2,"data":["event","payload"],"nsp":"/"} as 2["event","payload"] +1ms
socket.io-redis ignore same uid +0ms
Jika proses master dan anak Anda keduanya menampilkan pesan parser yang sama, maka aplikasi Anda diskalakan dengan benar.
Seharusnya tidak ada masalah dengan pengaturan Anda jika Anda memancarkan dari satu pekerja. Apa yang Anda lakukan adalah memancarkan dari keempat pekerja, dan karena Redis mempublikasikan/berlangganan, pesan tidak diduplikasi, tetapi ditulis empat kali, seperti yang Anda minta aplikasi lakukan. Berikut adalah diagram sederhana tentang apa yang dilakukan Redis:
Client <-- Worker 1 emit --> Redis
Client <-- Worker 2 <----------|
Client <-- Worker 3 <----------|
Client <-- Worker 4 <----------|
Seperti yang Anda lihat, ketika Anda memancarkan dari seorang pekerja, itu akan menerbitkan emisi ke Redis, dan itu akan dicerminkan dari pekerja lain, yang telah berlangganan database Redis. Ini juga berarti Anda dapat menggunakan beberapa server soket yang terhubung dengan instans yang sama, dan emisi pada satu server akan diaktifkan di semua server yang terhubung.
Dengan cluster, ketika klien terhubung, itu akan terhubung ke salah satu dari empat pekerja Anda, bukan keempatnya. Itu juga berarti apa pun yang Anda pancarkan dari pekerja itu hanya akan ditampilkan sekali kepada klien. Jadi ya, aplikasi sedang diskalakan, tetapi cara Anda melakukannya, Anda memancarkan dari keempat pekerja, dan database Redis membuatnya seolah-olah Anda memanggilnya empat kali pada satu pekerja. Jika klien benar-benar terhubung ke keempat instans soket Anda, mereka akan menerima enam belas pesan per detik, bukan empat.
Jenis penanganan soket tergantung pada jenis aplikasi yang akan Anda miliki. Jika Anda akan menangani klien secara individual, maka Anda seharusnya tidak memiliki masalah, karena peristiwa koneksi hanya akan diaktifkan untuk satu pekerja per satu klien. Jika Anda membutuhkan "detak jantung" global, maka Anda dapat memiliki penangan soket dalam proses master Anda. Karena pekerja mati ketika proses master mati, Anda harus mengimbangi beban koneksi dari proses master, dan membiarkan anak-anak menangani koneksi. Ini contohnya:
var cluster = require('cluster');
var os = require('os');
if (cluster.isMaster) {
// we create a HTTP server, but we do not use listen
// that way, we have a socket.io server that doesn't accept connections
var server = require('http').createServer();
var io = require('socket.io').listen(server);
var RedisStore = require('socket.io/lib/stores/redis');
var redis = require('socket.io/node_modules/redis');
io.set('store', new RedisStore({
redisPub: redis.createClient(),
redisSub: redis.createClient(),
redisClient: redis.createClient()
}));
setInterval(function() {
// all workers will receive this in Redis, and emit
io.sockets.emit('data', 'payload');
}, 1000);
for (var i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
}
if (cluster.isWorker) {
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var RedisStore = require('socket.io/lib/stores/redis');
var redis = require('socket.io/node_modules/redis');
io.set('store', new RedisStore({
redisPub: redis.createClient(),
redisSub: redis.createClient(),
redisClient: redis.createClient()
}));
io.sockets.on('connection', function(socket) {
socket.emit('data', 'connected to worker: ' + cluster.worker.id);
});
app.listen(80);
}
Dalam contoh, ada lima contoh Socket.IO, satu menjadi master, dan empat menjadi anak. Server master tidak pernah memanggil listen()
jadi tidak ada koneksi overhead pada proses itu. Namun, jika Anda memanggil emit pada proses master, itu akan dipublikasikan ke Redis, dan empat proses pekerja akan melakukan emisi pada klien mereka. Ini mengimbangi beban koneksi ke pekerja, dan jika pekerja mati, logika aplikasi utama Anda tidak akan tersentuh di master.
Perhatikan bahwa dengan Redis, semua emisi, bahkan di ruang nama atau ruangan akan diproses oleh proses pekerja lain seolah-olah Anda memicu emisi dari proses itu. Dengan kata lain, jika Anda memiliki dua instance Socket.IO dengan satu instance Redis, panggil emit()
pada soket di pekerja pertama akan mengirim data ke kliennya, sementara pekerja dua akan melakukan hal yang sama seperti jika Anda memanggil emit dari pekerja itu.