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;)
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
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? –
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