2014-05-20 22 views
6

CẬP NHẬT: Ngay cả khi kịch bản cụ thể này không thực tế, theo nhận xét, tôi vẫn quan tâm đến cách viết mô-đun sử dụng phân cụm mà không cần chạy lại quy trình gốc mỗi lần .Sử dụng cụm trong mô-đun Nút


Tôi đang cố gắng viết mô-đun Node.js có tên là mass-request để tăng tốc số lượng lớn yêu cầu HTTP bằng cách phân phối chúng cho các quy trình con.

Hy vọng của tôi là, ở bên ngoài, nó hoạt động như thế này.

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

Để bắt đầu, tôi đặt cùng một bộ xương cho mô-đun yêu cầu khối lượng.

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

Sau đó, tôi kiểm tra nó như vậy trong một kịch bản thử nghiệm:

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

tôi mong đợi để xem "hello từ hàng loạt yêu cầu!" đăng nhập bốn lần (thực sự là nó). Trước sự ngạc nhiên của tôi, tôi cũng thấy "hello from test.js" bốn lần. Rõ ràng là tôi không hiểu cách hoạt động của cluster.fork(). Liệu nó có lặp lại toàn bộ quá trình, không chỉ là hàm gọi nó lần đầu tiên?

Nếu có, làm cách nào để sử dụng phân cụm trong một mô-đun mà không làm phiền người sử dụng mô-đun đó với logic đa xử lý lộn xộn?

+1

Làm thế nào sẽ chạy theo yêu cầu trong chủ đề trẻ em trình js giúp? Các yêu cầu Http đã tồn tại bên ngoài luồng js. Xem http://nodejs.org/api/http.html#http_class_http_agent – generalhenry

+0

Thú vị. Vì vậy, có hai hoặc nhiều quy trình phân chia công việc của nhiều cuộc gọi URL sẽ không đẩy nhanh quá trình này? Điều gì về một sợi để thực hiện tất cả các cuộc gọi và một chủ đề khác để xử lý các phản hồi? –

+1

Lý do duy nhất để sử dụng nhiều chủ đề js là nếu các chủ đề js là nút cổ chai. Do bản chất không đồng bộ của node.js hiếm khi xảy ra khi io cũng có trong hình. Vì vậy, tắt chế biến cho trẻ em chỉ có ý nghĩa nếu bạn đang làm công việc cpu chuyên sâu như mật mã. Mozilla persona là một ví dụ điển hình. – generalhenry

Trả lời

4

Tôi tin rằng những gì bạn đang tìm kiếm là trong setupMaster

Từ các tài liệu:

cluster.setupMaster ([Cài đặt])

  • cài đặt Đối tượng
    • exec Đường dẫn tệp chuỗi tới tệp công nhân. (Mặc định = process.argv [1])
    • args Đối số chuỗi mảng được truyền cho công nhân. (Mặc định = process.argv.slice (2))
    • im lặng Boolean có hay không gửi đầu ra tới stdio của cha mẹ. (Mặc định = sai)

setupMaster được sử dụng để thay đổi hành vi 'nĩa' mặc định. Khi được gọi, các cài đặt sẽ có mặt trong cluster.settings

Bằng cách sử dụng thuộc tính exec bạn có thể khởi động từ một mô-đun khác.

Quan trọng: là trạng thái tài liệu, điều này chỉ có thể được gọi một lần. Nếu bạn đang phụ thuộc vào hành vi này cho mô-đun của bạn, thì người gọi không thể sử dụng cluster hoặc toàn bộ sự việc bị xé toạc.

Ví dụ:

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

nhân.js

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

đầu ra

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

Thanh lịch và đơn giản, cảm ơn bạn! –

+0

dòng đó là gì "return {add: function (url, cb) {}}" đang hoạt động? –

+0

cũng có thể một nhân viên trong cụm bằng chức năng này thực sự gửi phản hồi HTTP không? –

3

Mặc dù bản chất không đồng bộ của node.js làm cho nó trở nên tuyệt vời, nhưng nó vẫn chạy trong một chuỗi duy nhất trên máy chủ trong một vòng lặp sự kiện duy nhất. Đa luồng một ứng dụng node.js với cluster cho phép bạn chia rẽ các tiến trình con của ứng dụng thành các chủ đề riêng của chúng, cho phép bạn tận dụng tốt hơn máy chủ đa lõi. Tôi đã xây dựng một kiến ​​trúc máy chủ trò chơi trong khi ngược lại sử dụng cụm và zmq (ZeroMQ) để đa luồng và cho phép các quy trình dễ dàng gửi tin nhắn qua lại trên nhiều kênh khác nhau. Tôi đã đơn giản hóa kiến ​​trúc đó thành ví dụ dưới đây để hy vọng giúp minh họa cách node.js đa luồng có thể được đặt lại với nhau. Tôi xin lỗi nếu đó là một chút thô, đó là năm trước và tôi là tương đối mới vào nút vào thời điểm đó;)

Lý tưởng nhất, bạn không muốn làm tổ tất cả mọi thứ cho chủ/con trong một kịch bản duy nhất, nhưng tôi Như bạn đã đề cập trong bình luận của bạn, tôi đã đưa ra một ví dụ tốt về phân cụm, nhưng không phải là một ví dụ phù hợp với trường hợp sử dụng cụ thể của bạn khi gửi đi mọi thứ xung quanh . Tôi không có nhiều thời gian, nên tôi thích nghi với ví dụ của tôi để làm cho nó phù hợp với nhu cầu của bạn khá nhanh chóng. Cung cấp cho này một shot:

hàng loạt 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(); 

Đặt những người trong cùng một thư mục và chạy node demo.js.

Tôi cũng nên chỉ ra rằng kể từ khi cơ sở này đã được kéo ra từ một trong những dự án khác của tôi rằng sử dụng [0MQ] [http://zeromq.org/], bạn sẽ cần phải được cài đặt cùng với [Node.js mô-đun cho nó] [https://github.com/JustinTulloss/zeromq.node]npm install zmq và rõ ràng là mô-đun cụm.Bạn có thể hoán đổi các phần ZMQ cho bất kỳ phương thức giao tiếp liên bộ nào khác mà bạn mong muốn. Điều này chỉ xảy ra là một trong những tôi đã quen thuộc và đã sử dụng.

Tổng quan ngắn gọn: Chuỗi chủ AKA tập lệnh gọi phương thức run() sẽ quay lên con X (có thể được đặt bằng cách gọi setThreads). Những đứa trẻ đó báo cáo lại luồng chủ thông qua các ổ cắm ZeroMQ khi chúng khởi tạo xong. Khi tất cả các luồng đã sẵn sàng, tập lệnh chính sẽ gửi các url đến các con để chúng có thể chạy và tìm nạp HTML. Chúng trả về HTML cho master khi nó chuyển nó vào hàm gọi lại thích hợp cho URL đó và sau đó gửi một URL khác đến tập lệnh con. Trong khi nó không phải là một giải pháp hoàn hảo, các chức năng gọi lại vẫn sẽ tắc nghẽn trong luồng chính (chủ) vì bạn không thể dễ dàng chuyển chúng sang một luồng khác. Những callbacks này có thể chứa các đóng/biến/etc có thể không hoạt động đúng bên ngoài chuỗi chủ đề mà không có một số loại cơ chế chia sẻ đối tượng.

Anywho, nếu bạn quay lên bản demo nhỏ của tôi ở đây bạn sẽ thấy 4 chủ đề "xử lý" các url (họ không thực sự tải các url vì lợi ích đơn giản).

Hy vọng rằng sẽ giúp;)

+0

Cảm ơn bạn! Đây là một ví dụ tuyệt vời về phân cụm. Đó thực sự không phải là vấn đề của tôi. Đó là cách để làm điều này loại trong một mô-đun đó là cần thiết từ một kịch bản khác nhau như vậy mà kịch bản mẹ không có lo lắng về việc kiểm tra cho dù đó là chủ hay không. Xem ví dụ mã trong bài đăng gốc. Chúc mừng! –

+0

@ChrisWilson Phải, bạn là sir. Tôi thích ví dụ ban đầu yêu cầu của bạn một cách tốt nhất tôi có thể trong thời gian nhỏ, tôi đã phải làm như vậy: X Đây là một bản demo làm việc và tôi nghĩ nó khá tốt đáp ứng những gì bạn đang tìm kiếm để làm;) File 'demo.js' là những gì người dùng của mô-đun của bạn về cơ bản sẽ phải làm, mọi thứ khác chạy trong nền theo yêu cầu. – Brian

+0

Tôi ước tôi có thể chia tiền thưởng với bạn, bởi vì tôi đã học được rất nhiều từ câu trả lời chu đáo của bạn. Cảm ơn bạn! –

Các vấn đề liên quan