Redis
 sql >> डेटाबेस >  >> NoSQL >> Redis

क्लस्टर का उपयोग करके Socket.IO को कई Node.js प्रक्रियाओं में स्केल करना

संपादित करें: Socket.IO 1.0+ में, एक से अधिक Redis क्लाइंट के साथ एक स्टोर सेट करने के बजाय, अब एक सरल Redis एडेप्टर मॉड्यूल का उपयोग किया जा सकता है।

var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

नीचे दिखाया गया उदाहरण कुछ इस तरह दिखेगा:

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

यदि आपके पास एक मास्टर नोड है जिसे अन्य Socket.IO प्रक्रियाओं में प्रकाशित करने की आवश्यकता है, लेकिन स्वयं सॉकेट कनेक्शन स्वीकार नहीं करता है, तो socket.io-redis के बजाय socket.io-emitter का उपयोग करें।

यदि आपको स्केलिंग में समस्या हो रही है, तो अपने नोड एप्लिकेशन को DEBUG=* . के साथ चलाएं . Socket.IO अब डिबग लागू करता है जो Redis अडैप्टर डिबग संदेशों का प्रिंट आउट भी लेगा। उदाहरण आउटपुट:

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

यदि आपका मास्टर और चाइल्ड दोनों प्रोसेस समान पार्सर संदेश प्रदर्शित करते हैं, तो आपका एप्लिकेशन ठीक से स्केलिंग कर रहा है।

यदि आप एक ही कार्यकर्ता से उत्सर्जन कर रहे हैं तो आपके सेटअप में कोई समस्या नहीं होनी चाहिए। आप जो कर रहे हैं वह सभी चार श्रमिकों से निकल रहा है, और रेडिस के प्रकाशन/सदस्यता के कारण, संदेशों को डुप्लिकेट नहीं किया जाता है, लेकिन चार बार लिखा जाता है, जैसा आपने आवेदन करने के लिए कहा था। रेडिस क्या करता है इसका एक सरल आरेख यहां दिया गया है:

Client  <--  Worker 1 emit -->  Redis
Client  <--  Worker 2  <----------|
Client  <--  Worker 3  <----------|
Client  <--  Worker 4  <----------|

जैसा कि आप देख सकते हैं, जब आप किसी कार्यकर्ता से उत्सर्जन करते हैं, तो यह रेडिस को उत्सर्जन प्रकाशित करेगा, और इसे अन्य श्रमिकों से प्रतिबिंबित किया जाएगा, जिन्होंने रेडिस डेटाबेस की सदस्यता ली है। इसका मतलब यह भी है कि आप एक ही इंस्टेंस से जुड़े कई सॉकेट सर्वर का उपयोग कर सकते हैं, और एक सर्वर पर एक एमिट सभी कनेक्टेड सर्वर पर सक्रिय हो जाएगा।

क्लस्टर के साथ, जब कोई क्लाइंट कनेक्ट होता है, तो यह आपके चार कर्मचारियों में से एक से कनेक्ट होगा, न कि सभी चार से। इसका मतलब यह भी है कि आप उस कार्यकर्ता से जो कुछ भी उत्सर्जित करते हैं वह ग्राहक को केवल एक बार दिखाया जाएगा। तो हाँ, एप्लिकेशन स्केलिंग कर रहा है, लेकिन जिस तरह से आप इसे कर रहे हैं, आप सभी चार श्रमिकों से निकल रहे हैं, और रेडिस डेटाबेस ऐसा बना रहा है जैसे आप इसे एक ही कार्यकर्ता पर चार बार कॉल कर रहे थे। यदि कोई क्लाइंट वास्तव में आपके चारों सॉकेट इंस्टेंस से जुड़ा है, तो उन्हें एक सेकंड में सोलह संदेश प्राप्त होंगे, चार नहीं।

सॉकेट हैंडलिंग का प्रकार आपके पास होने वाले एप्लिकेशन के प्रकार पर निर्भर करता है। यदि आप ग्राहकों को व्यक्तिगत रूप से संभालने जा रहे हैं, तो आपको कोई समस्या नहीं होनी चाहिए, क्योंकि कनेक्शन घटना केवल एक कार्यकर्ता प्रति एक ग्राहक के लिए सक्रिय होगी। यदि आपको वैश्विक "दिल की धड़कन" की आवश्यकता है, तो आपके पास अपनी मास्टर प्रक्रिया में सॉकेट हैंडलर हो सकता है। चूंकि मास्टर प्रक्रिया के समाप्त होने पर श्रमिक मर जाते हैं, इसलिए आपको मास्टर प्रक्रिया के कनेक्शन लोड को ऑफसेट करना चाहिए, और बच्चों को कनेक्शन को संभालने देना चाहिए। यहां एक उदाहरण दिया गया है:

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

उदाहरण में, पांच Socket.IO उदाहरण हैं, एक मास्टर है, और चार बच्चे हैं। मास्टर सर्वर कभी कॉल नहीं करता listen() इसलिए उस प्रक्रिया पर कोई कनेक्शन ओवरहेड नहीं है। हालाँकि, यदि आप मास्टर प्रक्रिया पर एक उत्सर्जन कहते हैं, तो इसे रेडिस पर प्रकाशित किया जाएगा, और चार कार्यकर्ता प्रक्रियाएँ अपने ग्राहकों पर उत्सर्जन का प्रदर्शन करेंगी। यह श्रमिकों के लिए कनेक्शन लोड को ऑफसेट करता है, और यदि एक कर्मचारी की मृत्यु हो जाती है, तो आपका मुख्य अनुप्रयोग तर्क मास्टर में अछूता रहेगा।

ध्यान दें कि रेडिस के साथ, नामस्थान या कमरे में भी सभी उत्सर्जन को अन्य कार्यकर्ता प्रक्रियाओं द्वारा संसाधित किया जाएगा जैसे कि आपने उस प्रक्रिया से उत्सर्जन को ट्रिगर किया था। दूसरे शब्दों में, यदि आपके पास एक Redis उदाहरण के साथ दो Socket.IO इंस्टेंस हैं, तो emit() को कॉल करें। सॉकेट पर पहले कार्यकर्ता अपने ग्राहकों को डेटा भेजेगा, जबकि कार्यकर्ता दो ऐसा ही करेगा जैसे कि आप उस कार्यकर्ता से उत्सर्जन कहते हैं।



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. मैं एसएसएल का उपयोग करने के लिए जेडिसकनेक्शन फैक्ट्री को कैसे कॉन्फ़िगर करूं ताकि मुझे त्रुटि न मिले:जेडिसडेटा अपवाद:ईआरआर अनएन्क्रिप्टेड कनेक्शन प्रतिबंधित है?

  2. मैं फंसे/बासी रेस्क्यू कर्मचारियों को कैसे हटाऊं?

  3. रेडिस में एकतरफा हैश स्लॉट को कैसे ठीक करें

  4. रेडिस पायथन में कई कनेक्शन बनाना और प्रबंधित करना

  5. अजवाइन कार्य से मल्टीप्रोसेसिंग पूल का उपयोग अपवाद उठाता है