2011-12-02 9 views
6

Używam Node.js, Socket.io z Redisstore, Cluster od użytkowników Socket.io i Redis.Otrzymuję zduplikowane wiadomości w mojej klastrowej aplikacji node.js/socket.io/redis pub/sub

Posiadam aplikację pub/sub, która działa dobrze tylko na jednym węźle Node.js. Ale, gdy jest pod dużym obciążeniem, maksymalizuje tylko jeden rdzeń serwera, ponieważ Node.js nie jest napisany dla maszyn wielordzeniowych.

Jak widać poniżej, teraz używam modułu Cluster od LearnBoost, tych samych osób, które tworzą Socket.io.

Ale kiedy uruchamiam 4 procesy robocze, każdy klient przeglądarki, który wchodzi i subskrybuje, otrzymuje 4 kopie każdej wiadomości opublikowanej w Redis. Jeśli istnieją trzy procesy robocze, istnieją trzy kopie.

Zgaduję, że muszę przenieść funkcje pub/sub redis do pliku cluster.js w jakiś sposób.

Cluster.js

var cluster = require('./node_modules/cluster'); 

cluster('./app') 
    .set('workers', 4) 
    .use(cluster.logger('logs')) 
    .use(cluster.stats()) 
    .use(cluster.pidfiles('pids')) 
    .use(cluster.cli()) 
    .use(cluster.repl(8888)) 
    .listen(8000); 

App.js

redis = require('redis'), 
sys = require('sys'); 

var rc = redis.createClient(); 

var path = require('path') 
    , connect = require('connect') 
    , app = connect.createServer(connect.static(path.join(__dirname, '../'))); 

// require the new redis store 
var sio = require('socket.io') 
    , RedisStore = sio.RedisStore 
    , io = sio.listen(app); 

io.set('store', new RedisStore);io.sockets.on('connection', function(socket) { 
    sys.log('ShowControl -- Socket connected: ' + socket.id); 

    socket.on('channel', function(ch) { 
     socket.join(ch) 
     sys.log('ShowControl -- ' + socket.id + ' joined channel: ' + ch); 
    }); 

    socket.on('disconnect', function() { 
     console.log('ShowControll -- Socket disconnected: ' + socket.id); 
    }); 
}); 

rc.psubscribe('showcontrol_*'); 

rc.on('pmessage', function(pat, ch, msg) { 
    io.sockets.in(ch).emit('show_event', msg); 
    sys.log('ShowControl -- Publish sent to channel: ' + ch); 
}); 

// cluster compatiblity 
if (!module.parent) { 
    app.listen(process.argv[2] || 8081); 
    console.log('Listening on ', app.address()); 
} else { 
    module.exports = app; 
} 

client.html

<script src="http://localhost:8000/socket.io/socket.io.js"></script> 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> 
<script> 
    var socket = io.connect('localhost:8000'); 
    socket.emit('channel', 'showcontrol_106'); 
    socket.on('show_event', function (msg) { 
     console.log(msg); 
     $("body").append('<br/>' + msg); 
    }); 
</script> 

Odpowiedz

3

Okazało się, że to nie jest problem z Node.js/Socket.io, po prostu robiłem to zupełnie niewłaściwie.

Nie tylko publikowałem na serwerze Redis spoza stosu Węzłów/Socket, wciąż byłem subskrybowany bezpośrednio do kanału Redis. Na obu końcach sytuacji pub/sub pomijałem "dobroć" w klastrze Socket.io z Redis Store na zapleczu ".

Stworzyłem więc małą aplikację (z Node.js/Socket.io/Express), która pobierała wiadomości z mojej aplikacji Rails i "ogłaszała" je w pokoju Socket.io za pomocą modułu socket.io-announce. Teraz za pomocą Socket.io magii routingu, każdy pracownik węzła otrzymywałby i wysyłałby tylko wiadomości bezpośrednio do przeglądarek podłączonych do nich. Innymi słowy, nie ma więcej duplikatów wiadomości, ponieważ zarówno pub, jak i sub wydarzyły się w stosie Node.js/Socket.io.

Po tym jak mój kod zostanie wyczyszczony, umieszczę przykład na github gdzieś.

+1

Czy możesz napisać, co zrobiłeś, aby rozwiązać ten problem? –

+0

Możesz zobaczyć, co tutaj zrobiliśmy: https://github.com/StageIt/redis Jednak przywróciliśmy go do jednego węzła z powodu błędu w pliku socket.io. Oto omówienie tego błędu, może to być już naprawione: https://github.com/LearnBoost/socket.io/issues/438#issuecomment-3371390 – messick

3

byłem walczy z klastra i socket.io. Za każdym razem, gdy używam funkcji cluster (używam wbudowanego klastra Nodejs), otrzymuję wiele problemów z wydajnością i problemów z socket.io.

Podczas próby zbadania tego, kopałem wokół raportów o błędach i podobnych na git socket.io i każdy używający klastrów lub zewnętrznych równoważników obciążenia do swoich serwerów wydaje się mieć problemy z socket.io.

Wydaje się, że stwarza problem "klient, którego klient nie powinien ręcznie potwierdzać, powinien się ponownie połączyć", co można zobaczyć, jeśli zwiększysz szczegółowe rejestrowanie. Pojawia się wiele razy, gdy socket.io działa w klastrze, więc myślę, że powraca do tego. I.E klient łączy się z randomizowaną instancją w klastrze socket.io za każdym razem, gdy wykonuje nowe połączenie (wykonuje kilka połączeń http/socket/flash podczas autoryzacji, a później cały czas, gdy pobiera nowe dane).

Na razie wróciłem do używania tylko jednego procesu socket.io na raz, może to być błąd, ale może to być również wada sposobu budowania gniazda socket.io.

Dodano: Moim sposobem rozwiązania tego problemu w przyszłości będzie przypisanie unikalnego portu do każdej instancji socket.io wewnątrz klastra, a następnie wybór portu pamięci podręcznej po stronie klienta.

Powiązane problemy