2014-05-20 29 views
6

अद्यतन: यहां तक ​​कि अगर यह विशेष परिदृश्य यथार्थवादी नहीं है, तो टिप्पणियों के अनुसार, मुझे अभी भी दिलचस्पी है कि कोई मॉड्यूल कैसे लिख सकता है जो प्रत्येक बार पेरेंट प्रक्रिया को पुन: पेश किए बिना क्लस्टरिंग का उपयोग करता है ।नोड मॉड्यूल में क्लस्टर का उपयोग


मैं एक Node.js मॉड्यूल mass-request कहा जाता है कि उन्हें बच्चे प्रक्रियाओं के लिए वितरण के द्वारा HTTP अनुरोध की बड़ी संख्या को गति लिखने की कोशिश कर रहा हूँ।

मेरी आशा यह है कि, बाहर की ओर, यह इस तरह काम करता है।

var mr = require("mass-request"), 
    scraper = mr(); 

for (var i = 0; i < my_urls_to_visit.length; i += 1) { 
    scraper.add(my_urls_to_visit[i], function(resp) { 
     // do something with response 
    } 
} 

आरंभ करने के लिए, मैंने बड़े पैमाने पर अनुरोध मॉड्यूल के लिए एक कंकाल रखा।

var cluster = require("cluster"), 
    numCPUs = require("os").cpus().length; 

module.exports = function() { 
    console.log("hello from mass-request!"); 
    if (cluster.isMaster) { 
     for (var i = 0; i < numCPUs; i += 1) { 
      var worker = cluster.fork();    
     } 

     return { 
      add: function(url, cb) {}  
     }  
    } else { 
     console.log("worker " + process.pid + " is born!"); 
    } 
} 

तो मैं एक परीक्षण लिपि में इतना है कि यह परीक्षण:

var m = mr(); 
console.log("hello from test.js!", m); 

मैं देखना चाहता उम्मीद "हैलो बड़े पैमाने पर अनुरोध से!" चार बार लॉग इन किया (वास्तव में यह है)। मेरे आश्चर्य के लिए, मैं चार बार "test.js से हैलो" भी देखता हूं। स्पष्ट रूप से मुझे समझ में नहीं आता कि cluster.fork() काम करता है। क्या यह पूरी प्रक्रिया को दोबारा शुरू कर रहा है, न केवल उस समारोह को जो इसे पहली बार बुलाता है?

यदि हां, तो मॉड्यूल में क्लस्टरिंग का उपयोग करने वाले व्यक्ति को परेशान किए बिना उस मॉड्यूल का उपयोग करने वाले व्यक्ति को परेशान किए बिना कैसे गड़बड़ बहु-प्रक्रिया तर्क के साथ उपयोग किया जाता है?

+1

कैसे बच्चे की प्रक्रिया js धागे में अनुरोध मदद से चल रहा होगा? एचटीपी अनुरोध पहले से ही जेएस धागे के बाहर मौजूद हैं। Http://nodejs.org/api/http.html#http_class_http_agent – generalhenry

+0

दिलचस्प देखें। तो कई यूआरएल कॉल के काम को विभाजित करने में दो या दो से अधिक प्रक्रियाएं होने से प्रक्रिया तेज नहीं होगी? प्रतिक्रियाओं को संभालने के लिए सभी कॉल और दूसरे को बनाने के लिए एक थ्रेड के बारे में क्या? –

+1

एकाधिक जेएस धागे का उपयोग करने का एकमात्र कारण यह है कि जेएस धागे बाधाएं हैं। Node.js की एसिंक्रोनस प्रकृति को देखते हुए शायद ही कभी यह मामला है जब तस्वीर में आईओ भी है। तो बच्चों को प्रसंस्करण बंद करना केवल तभी समझ में आता है जब आप क्रिप्टो जैसे सीपीयू गहन काम कर रहे हों। मोज़िला व्यक्तित्व एक अच्छा उदाहरण है। – generalhenry

उत्तर

4

मेरा मानना ​​है कि के लिए setupMaster

डॉक्स से में है कि तुम क्या देख रहे हैं:

cluster.setupMaster ([सेटिंग])

  • सेटिंग्स ऑब्जेक्ट
    • निष्पादन फ़ाइल के लिए निष्पादन स्ट्रिंग फ़ाइल पथ। (डिफ़ॉल्ट = process.argv [1])
    • तर्क देता है कि ऐरे स्ट्रिंग तर्क कार्यकर्ता को पास किया गया है। (डिफ़ॉल्ट = process.argv.slice (2))
    • मूक बूलियन माता-पिता के stdio को आउटपुट भेजने के लिए या नहीं। (डिफ़ॉल्ट = false)

setupMaster डिफ़ॉल्ट 'कांटा' व्यवहार बदलने के लिए प्रयोग किया जाता है। एक बार कॉल करने के बाद, सेटिंग्स क्लस्टर.सेटिंग

निष्पादन संपत्ति का उपयोग करके आप अपने कर्मचारियों को एक अलग मॉड्यूल से लॉन्च कर सकते हैं।

महत्वपूर्ण: दस्तावेज़ राज्य के रूप में, इसे केवल एक बार बुलाया जा सकता है। आप अपने मॉड्यूल के लिए इस व्यवहार पर निर्भर करता है, तो फोन करने वाले cluster का उपयोग नहीं किया जा सकता है या पूरी बात अलग हो जाता है।

उदाहरण के लिए:

index.js

var cluster = require("cluster"), 
    path = require("path"), 
    numCPUs = require("os").cpus().length; 

console.log("hello from mass-request!"); 
if (cluster.isMaster) { 
    cluster.setupMaster({ 
    exec: path.join(__dirname, 'worker.js') 
    }); 

    for (var i = 0; i < numCPUs; i += 1) { 
    var worker = cluster.fork(); 
    } 

    return { 
    add: function (url, cb) { 
    } 
    } 
} else { 
    console.log("worker " + process.pid + " is born!"); 
} 

कार्यकर्ता।js

console.log("worker " + process.pid + " is born!"); 

उत्पादन

node index.js 
hello from mass-request! 
worker 38821 is born! 
worker 38820 is born! 
worker 38822 is born! 
worker 38819 is born! 
+0

सुरुचिपूर्ण और सरल, धन्यवाद! –

+0

वह लाइन क्या है "वापसी {जोड़ें: फ़ंक्शन (यूआरएल, सीबी) {}}" कर रहा है? –

+0

भी, क्या इस कार्यक्षमता का उपयोग कर क्लस्टर में एक कर्मचारी वास्तव में एक HTTP प्रतिक्रिया भेज सकता है? –

3

हालांकि यह सच है कि node.js की एसिंक्रोनस प्रकृति इसे शानदार बनाती है, यह अभी भी एक ईवेंट लूप में सर्वर पर एक ही थ्रेड में चलती है। क्लस्टर के साथ एक node.js ऐप को मल्टीथ्रेड करने से आप ऐप की बाल प्रक्रियाओं को अपने स्वयं के धागे में फेंकने की अनुमति देते हैं, जिससे आप बहु-कोर सर्वर का बेहतर उपयोग कर सकते हैं। मैंने कुछ समय पहले एक गेम सर्वर आर्किटेक्चर बनाया था जो क्लस्टर और जेएमक्यू (ज़ीरोएमक्यू) को मल्टीथ्रेड करने के लिए इस्तेमाल करता था और प्रक्रियाओं को आसानी से विभिन्न चैनलों पर संदेशों को आसानी से भेजने में सक्षम बनाता था। मैंने आशा व्यक्त की है कि आर्किटेक्चर को नीचे दिए गए उदाहरण में सरलीकृत करने में मदद मिलेगी कि कैसे multithreaded node.js को एक साथ रखा जा सकता है। मैं माफी माँगता हूँ अगर यह थोड़ा किसी न किसी तरह है, यह साल पहले था और मैं अपेक्षाकृत समय में नोड के लिए नया था;)

आदर्श रूप में, आप एक ही लिपि में मास्टर/बच्चे के लिए घोंसला सब कुछ नहीं करना चाहते हैं, लेकिन मैं यह अनुमान लगाया गया कि यह आपको कॉपी/पेस्ट/चलाने के लिए सबसे आसान तरीका था :)

जैसा कि आपने अपनी टिप्पणी में उल्लेख किया है, मैंने क्लस्टरिंग का एक अच्छा उदाहरण दिया है, लेकिन आपके विशिष्ट उपयोग मामले में फिट नहीं है, जहां तक ​​आसपास के सब कुछ भेजना । मेरे पास बहुत समय नहीं था, इसलिए मैंने इसे अपनी आवश्यकताओं के लिए काम करने के लिए अपना उदाहरण अनुकूलित किया। इस एक शॉट दे दो:

बड़े पैमाने पर request.js

var cluster = require('cluster'); 
var zmq = require('zmq'); 

module.exports = { 
    _childId : null, 
    _urls : [], 
    _threadCount : 1, 
    _readyThreads : 0, 
    _callbacks : {}, 
    zmqReceive : null, //the socket we receive on for this thread 
    zmqMaster : null, //the socket to the master 
    zmqChildren : {}, //an object storing the sockets for the children 
    setThreads : function(threadCount) { 
     this._threadCount = threadCount; 
    }, 
    add : function(url , cb) { 
     this._urls.push({url: url, cb : cb }); 
    }, 
    run : function() { 

     if(cluster.isMaster) { 

      this._masterThread(); 

     } else { 

      this._childThread(); 

     } 

    }, 
    _masterThread : function() { 

     console.log('Master Process Starting Up'); 

     this.zmqReceive = zmq.socket('pull').bindSync('ipc://master.ipc'); 

     //bind handler for messages coming into this process using closure to allow us to access the massrequest object inside the callback 
     (function(massRequest) { 
      this.zmqReceive.on('message' , function(msg) { 

       msg = JSON.parse(msg); 

       //was this an online notification? 
       if(msg && msg.status == 'Online') { 
        massRequest._threadReady(); 
        return; //we're done 
       } 
       if(msg && msg.html) { 
        //this was a response from a child, call the callback for it 
        massRequest._callbacks[ msg.sender ].call(massRequest , msg.html); 
        //send the child another URL 
        massRequest._sendUrlToChild(msg.sender); 
       } 

      }); 
     }).call(this , this); 

     //fork 4 child processes and set up the sending sockets for them 
     for(var i=0; i < this._threadCount; ++i) { 
      //set up the sending socket 
      this.zmqChildren[i] = zmq.socket('push').connect('ipc://child_' + i + '.ipc'); 
      //fork the process and pass it an id 
      cluster.fork({ 
       _childId:i 
      }); 
     } 

    }, 
    _sendUrlToChild : function(child) { 
     //if there's no urls left, return (this would also be a good place to send a message to the child to exit gracefully) 
     if(!this._urls.length) return; 
     //grab a url to process 
     var item = this._urls.pop(); 
     //set the callback for the child 
     this._callbacks[child] = item.cb; 
     this.zmqChildren[child].send(JSON.stringify({ url:item.url })); 
    }, 
    _processUrls : function() { 
     for(var i=0; i < this._threadCount; ++i) { 
      this._sendUrlToChild(i); 
     } 
    }, 
    _threadReady : function() { 
     if(++this._readyThreads >= this._threadCount) { 
      //all threads are ready, send out urls to start the mayhem 
      console.log('All threads online, starting URL processing'); 
      this._processUrls(); 
     } 
    }, 
    _childProcessUrl : function(url) { 
     console.log('Child Process ' + this.childId + ' Handling URL: ' + url); 
     //do something here to scrape your content however you see fit 
     var html = 'HTML'; 
     this.zmqMaster.send(JSON.stringify({ sender:this.childId, html:html })); 
    }, 
    _childThread : function() { 

     //get the child id that was passed from cluster 
     this.childId = process.env._childId; 

     console.log('Child Process ' + this.childId + ' Starting Up'); 

     //bind the pull socket to receive messages to this process 
     this.zmqReceive = zmq.socket('pull').bindSync('ipc://child_' + this.childId + '.ipc'); 

     //bind the push socket to send to the master 
     this.zmqMaster = zmq.socket('push').connect('ipc://master.ipc'); 

     //bind handler for messages coming into this process 
     (function(massRequest) { 
      this.zmqReceive.on('message' , function(msg) { 

       msg = JSON.parse(msg); 

       console.log('Child ' + this.childId + ': ' + msg); 

       //handle the url 
       if(msg && msg.url) massRequest._childProcessUrl(msg.url); 

      }); 
     }).call(this , this); 

     //let the master know we're done setting up 
     this.zmqMaster.send(JSON.stringify({sender:this.childId,status:'Online'})); 

    }, 
} 

demo.js

var mr = require('./mass-request.js'); 
mr.setThreads(4); 
mr.add('http://foo.com' , function(resp) { 
    console.log('http://foo.com is done'); 
}); 
mr.add('http://bar.com' , function(resp) { 
    console.log('http://bar.com is done'); 
}); 
mr.add('http://alpha.com' , function(resp) { 
    console.log('http://alpha.com is done'); 
}); 
mr.add('http://beta.com' , function(resp) { 
    console.log('http://beta.com is done'); 
}); 
mr.add('http://theta.com' , function(resp) { 
    console.log('http://theta.com is done'); 
}); 
mr.add('http://apples.com' , function(resp) { 
    console.log('http://apples.com is done'); 
}); 
mr.add('http://oranges.com' , function(resp) { 
    console.log('http://oranges.com is done'); 
}); 
mr.run(); 

एक ही फ़ोल्डर में उन रखो और node demo.js चलाते हैं।

मैं यह भी कहना चाहिए कि इस के बाद के आधार मेरे अन्य परियोजनाओं में से एक है कि [0MQ] का उपयोग [http://zeromq.org/] से खींचा गया था, तो आप उस [के साथ स्थापित की आवश्यकता होगी इसके लिए मॉड्यूल Node.js] [https://github.com/JustinTulloss/zeromq.node]npm install zmq और जाहिर है क्लस्टर मॉड्यूल।आप जेडएमक्यू भागों को इंटरप्रोसेस संचार की किसी भी अन्य विधि के लिए स्वैप कर सकते हैं जो आप चाहते हैं। यह सिर्फ एक होता है जिसे मैं परिचित था और इस्तेमाल किया था।

संक्षिप्त अवलोकन: मास्टर थ्रेड AKA स्क्रिप्ट जो रन() विधि को कॉल करती है वह एक्स बच्चों को स्पिन करेगी (सेट थ्रेड को कॉल करके सेट किया जा सकता है)। वे बच्चे ज़ीरोएमक्यू सॉकेट के माध्यम से मास्टर थ्रेड पर वापस रिपोर्ट करते हैं जब वे प्रारंभ करना समाप्त कर देते हैं। एक बार सभी धागे तैयार हो जाने के बाद, मास्टर स्क्रिप्ट बच्चों को यूआरएल भेजती है ताकि वे एचटीएमएल को चला सकें और ला सकें। वे एचटीएमएल को मास्टर पर लौटते हैं जहां यह उस यूआरएल के लिए उचित कॉलबैक फ़ंक्शन में भेजता है और फिर बच्चे स्क्रिप्ट में एक और यूआरएल भेजता है। हालांकि यह एक सही समाधान नहीं है, कॉलबैक फ़ंक्शन अभी भी मुख्य (मास्टर) थ्रेड में बाधा डालने जा रहे हैं क्योंकि आप आसानी से उन्हें किसी अन्य थ्रेड पर नहीं ले जा सकते हैं। उन कॉलबैक में क्लोजर/वेरिएबल्स/आदि हो सकते हैं जो बिना किसी ऑब्जेक्ट शेयरिंग मैकेनिज्म के पैरेंट थ्रेड के बाहर ठीक से काम नहीं कर सकते हैं।

Anywho, अगर तुम यहाँ मेरी छोटी डेमो ऊपर स्पिन आप 4 धागे "संसाधन" यूआरएल देखेंगे (वे वास्तव में सादगी खातिर यूआरएल लोड नहीं है)।

उम्मीद है कि मदद करता है कि;)

+0

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

+0

@ क्रिस विल्सन ठीक है आप महोदय हैं। मैंने आपकी जरूरतों के लिए मूल उदाहरण को अनुकूलित किया, जितना मैं कर सकता था उतना ही अच्छा था: एक्स यह एक कामकाजी डेमो है और मुझे लगता है कि यह बहुत अच्छी तरह से संतुष्ट करता है कि आप क्या करना चाहते थे;) 'demo.js' फ़ाइल आपके मॉड्यूल के उपयोगकर्ता को मूल रूप से करना होगा, अनुरोध के अनुसार पृष्ठभूमि में बाकी सबकुछ चलता है। – Brian

+0

मेरी इच्छा है कि मैं आपके साथ बक्षीस बांट सकता हूं, क्योंकि मैंने आपकी विचारशील प्रतिक्रिया से बहुत कुछ सीखा है। धन्यवाद! –

संबंधित मुद्दे