摘要:為了解決這個問題,可以在主進程部署事件和事件的監(jiān)聽函數(shù)。屬性返回一個布爾值,表示當(dāng)前進程是否為進程。主進程會連續(xù)兩次新建一個進程,然后關(guān)閉所有其他進程,顯示如下。一旦收到這個消息,進行完畢收尾清理工作再關(guān)閉。
概述 基本用法Node.js默認單進程運行,對于32位系統(tǒng)最高可以使用512MB內(nèi)存,對于64位最高可以使用1GB內(nèi)存。對于多核CPU的計算機來說,這樣做效率很低,因為只有一個核在運行,其他核都在閑置。cluster模塊就是為了解決這個問題而提出的。
cluster模塊允許設(shè)立一個主進程和若干個worker進程,由主進程監(jiān)控和協(xié)調(diào)worker進程的運行。worker之間采用進程間通信交換消息,cluster模塊內(nèi)置一個負載均衡器,采用Round-robin算法協(xié)調(diào)各個worker進程之間的負載。運行時,所有新建立的鏈接都由主進程完成,然后主進程再把TCP連接分配給指定的worker進程。
var cluster = require("cluster");
var os = require("os");
if (cluster.isMaster){
for (var i = 0, n = os.cpus().length; i < n; i += 1){
cluster.fork();
}
} else {
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world
");
}).listen(8000);
}
上面代碼先判斷當(dāng)前進程是否為主進程(cluster.isMaster),如果是的,就按照CPU的核數(shù),新建若干個worker進程;如果不是,說明當(dāng)前進程是worker進程,則在該進程啟動一個服務(wù)器程序。
上面這段代碼有一個缺點,就是一旦work進程掛了,主進程無法知道。為了解決這個問題,可以在主進程部署online事件和exit事件的監(jiān)聽函數(shù)。
var cluster = require("cluster");
if(cluster.isMaster) {
var numWorkers = require("os").cpus().length;
console.log("Master cluster setting up " + numWorkers + " workers...");
for(var i = 0; i < numWorkers; i++) {
cluster.fork();
}
cluster.on("online", function(worker) {
console.log("Worker " + worker.process.pid + " is online");
});
cluster.on("exit", function(worker, code, signal) {
console.log("Worker " + worker.process.pid + " died with code: " + code + ", and signal: " + signal);
console.log("Starting a new worker");
cluster.fork();
});
}
上面代碼中,主進程一旦監(jiān)聽到worker進程的exit事件,就會重啟一個worker進程。worker進程一旦啟動成功,可以正常運行了,就會發(fā)出online事件。
worker對象worker對象是cluster.fork()的返回值,代表一個worker進程。
它的屬性和方法如下。
(1)worker.id
worker.id返回當(dāng)前worker的獨一無二的進程編號。這個編號也是cluster.workers中指向當(dāng)前進程的索引值。
(2)worker.process
所有的worker進程都是用child_process.fork()生成的。child_process.fork()返回的對象,就被保存在worker.process之中。通過這個屬性,可以獲取worker所在的進程對象。
(3)worker.send()
該方法用于在主進程中,向子進程發(fā)送信息。
if (cluster.isMaster) {
var worker = cluster.fork();
worker.send("hi there");
} else if (cluster.isWorker) {
process.on("message", function(msg) {
process.send(msg);
});
}
上面代碼的作用是,worker進程對主進程發(fā)出的每個消息,都做回聲。
在worker進程中,要向主進程發(fā)送消息,使用process.send(message);要監(jiān)聽主進程發(fā)出的消息,使用下面的代碼。
process.on("message", function(message) {
console.log(message);
});
發(fā)出的消息可以字符串,也可以是JSON對象。下面是一個發(fā)送JSON對象的例子。
worker.send({
type: "task 1",
from: "master",
data: {
// the data that you want to transfer
}
});
cluster.workers對象
該對象只有主進程才有,包含了所有worker進程。每個成員的鍵值就是一個worker進程對象,鍵名就是該worker進程的worker.id屬性。
function eachWorker(callback) {
for (var id in cluster.workers) {
callback(cluster.workers[id]);
}
}
eachWorker(function(worker) {
worker.send("big announcement to all workers");
});
上面代碼用來遍歷所有worker進程。
當(dāng)前socket的data事件,也可以用id屬性識別worker進程。
socket.on("data", function(id) {
var worker = cluster.workers[id];
});
cluster模塊的屬性與方法
isMaster,isWorker
isMaster屬性返回一個布爾值,表示當(dāng)前進程是否為主進程。這個屬性由process.env.NODE_UNIQUE_ID決定,如果process.env.NODE_UNIQUE_ID為未定義,就表示該進程是主進程。
isWorker屬性返回一個布爾值,表示當(dāng)前進程是否為work進程。它與isMaster屬性的值正好相反。
fork()fork方法用于新建一個worker進程,上下文都復(fù)制主進程。只有主進程才能調(diào)用這個方法。
該方法返回一個worker對象。
kill()kill方法用于終止worker進程。它可以接受一個參數(shù),表示系統(tǒng)信號。
如果當(dāng)前是主進程,就會終止與worker.process的聯(lián)絡(luò),然后將系統(tǒng)信號法發(fā)向worker進程。如果當(dāng)前是worker進程,就會終止與主進程的通信,然后退出,返回0。
在以前的版本中,該方法也叫做 worker.destroy() 。
listening事件worker進程調(diào)用listening方法以后,“l(fā)istening”事件就傳向該進程的服務(wù)器,然后傳向主進程。
該事件的回調(diào)函數(shù)接受兩個參數(shù),一個是當(dāng)前worker對象,另一個是地址對象,包含網(wǎng)址、端口、地址類型(IPv4、IPv6、Unix socket、UDP)等信息。這對于那些服務(wù)多個網(wǎng)址的Node應(yīng)用程序非常有用。
cluster.on("listening", function (worker, address) {
console.log("A worker is now connected to " + address.address + ":" + address.port);
});
不中斷地重啟Node服務(wù)
思路
重啟服務(wù)需要關(guān)閉后再啟動,利用cluster模塊,可以做到先啟動一個worker進程,再把原有的所有work進程關(guān)閉。這樣就能實現(xiàn)不中斷地重啟Node服務(wù)。
首先,主進程向worker進程發(fā)出重啟信號。
workers[wid].send({type: "shutdown", from: "master"});
worker進程監(jiān)聽message事件,一旦發(fā)現(xiàn)內(nèi)容是shutdown,就退出。
process.on("message", function(message) {
if(message.type === "shutdown") {
process.exit(0);
}
});
下面是一個關(guān)閉所有worker進程的函數(shù)。
function restartWorkers() {
var wid, workerIds = [];
for(wid in cluster.workers) {
workerIds.push(wid);
}
workerIds.forEach(function(wid) {
cluster.workers[wid].send({
text: "shutdown",
from: "master"
});
setTimeout(function() {
if(cluster.workers[wid]) {
cluster.workers[wid].kill("SIGKILL");
}
}, 5000);
});
};
實例
下面是一個完整的實例,先是主進程的代碼master.js。
var cluster = require("cluster");
console.log("started master with " + process.pid);
// 新建一個worker進程
cluster.fork();
process.on("SIGHUP", function () {
console.log("Reloading...");
var new_worker = cluster.fork();
new_worker.once("listening", function () {
// 關(guān)閉所有其他worker進程
for(var id in cluster.workers) {
if (id === new_worker.id.toString()) continue;
cluster.workers[id].kill("SIGTERM");
}
});
});
上面代碼中,主進程監(jiān)聽SIGHUP事件,如果發(fā)生該事件就關(guān)閉其他所有worker進程。之所以是SIGHUP事件,是因為nginx服務(wù)器監(jiān)聽到這個信號,會創(chuàng)造一個新的worker進程,重新加載配置文件。另外,關(guān)閉worker進程時,主進程發(fā)送SIGTERM信號,這是因為Node允許多個worker進程監(jiān)聽同一個端口。
下面是worker進程的代碼server.js。
var cluster = require("cluster");
if (cluster.isMaster) {
require("./master");
return;
}
var express = require("express");
var http = require("http");
var app = express();
app.get("/", function (req, res) {
res.send("ha fsdgfds gfds gfd!");
});
http.createServer(app).listen(8080, function () {
console.log("http://localhost:8080");
});
使用時代碼如下。
$ node server.js started master with 10538 http://localhost:8080
然后,向主進程連續(xù)發(fā)出兩次SIGHUP信號。
$ kill -SIGHUP 10538
$ kill -SIGHUP 10538
主進程會連續(xù)兩次新建一個worker進程,然后關(guān)閉所有其他worker進程,顯示如下。
Reloading... http://localhost:8080 Reloading... http://localhost:8080
最后,向主進程發(fā)出SIGTERM信號,關(guān)閉主進程。
$ kill 10538
PM2模塊
PM2模塊是cluster模塊的一個包裝層。它的作用是盡量將cluster模塊抽象掉,讓用戶像使用單進程一樣,部署多進程Node應(yīng)用。
// app.js
var http = require("http");
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world");
}).listen(8080);
上面代碼是標(biāo)準(zhǔn)的Node架設(shè)Web服務(wù)器的方式,然后用PM2從命令行啟動這段代碼。
$ pm2 start app.js -i 4
上面代碼的i參數(shù)告訴PM2,這段代碼應(yīng)該在cluster_mode啟動,且新建worker進程的數(shù)量是4個。如果i參數(shù)的值是0,那么當(dāng)前機器有幾個CPU內(nèi)核,PM2就會啟動幾個worker進程。
如果一個worker進程由于某種原因掛掉了,會立刻重啟該worker進程。
# 重啟所有worker進程
$ pm2 reload all
每個worker進程都有一個id,可以用下面的命令查看單個worker進程的詳情。
$ pm2 show
正確情況下,PM2采用fork模式新建worker進程,即主進程fork自身,產(chǎn)生一個worker進程。pm2 reload命令則會用spawn方式啟動,即一個接一個啟動worker進程,一個新的worker啟動成功,再殺死一個舊的worker進程。采用這種方式,重新部署新版本時,服務(wù)器就不會中斷服務(wù)。
$ pm2 reload <腳本文件名>
關(guān)閉worker進程的時候,可以部署下面的代碼,讓worker進程監(jiān)聽shutdown消息。一旦收到這個消息,進行完畢收尾清理工作再關(guān)閉。
process.on("message", function(msg) {
if (msg === "shutdown") {
close_all_connections();
delete_logs();
server.close();
process.exit(0);
}
});
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7004.html
摘要:應(yīng)用實例創(chuàng)建完成后,會持續(xù)監(jiān)視這些實例。創(chuàng)建時,會創(chuàng)建了一個來托管應(yīng)用。通過請求,可以從集群的外部訪問一個服務(wù)。使用云提供商的負載均衡器如果支持,可以向外部暴露服務(wù)。使用在中部署第一個應(yīng)用本文以為例進行。目標(biāo)是將簡單的應(yīng)用部署在上運行。 Kubernetes是什么? Kubernetes是容器集群管理系統(tǒng),是一個開源的平臺,可以實現(xiàn)容器集群的自動化部署、自動擴縮容、維護等功能。可以在物...
摘要:應(yīng)用實例創(chuàng)建完成后,會持續(xù)監(jiān)視這些實例。創(chuàng)建時,會創(chuàng)建了一個來托管應(yīng)用。通過請求,可以從集群的外部訪問一個服務(wù)。使用云提供商的負載均衡器如果支持,可以向外部暴露服務(wù)。使用在中部署第一個應(yīng)用本文以為例進行。目標(biāo)是將簡單的應(yīng)用部署在上運行。 Kubernetes是什么? Kubernetes是容器集群管理系統(tǒng),是一個開源的平臺,可以實現(xiàn)容器集群的自動化部署、自動擴縮容、維護等功能??梢栽谖?..
摘要:關(guān)掉服務(wù),先啟動再啟動之前,修改,原因是插件和是兩個不同的線程,存在跨域問題。然后拷貝兩份的解壓包,修改如下集群名要一致,不同,防止端口號沖突。修改完成后,依次啟動主節(jié)點兩個普通節(jié)點及插件,訪問,會顯示三個節(jié)點分布式配置完成 文章用來開啟Node和Elasticsearch學(xué)習(xí)的新篇章,持續(xù)更新中...來源:慕課網(wǎng)瓦力老師課程 http://www.imooc.com/learn/88...
摘要:關(guān)掉服務(wù),先啟動再啟動之前,修改,原因是插件和是兩個不同的線程,存在跨域問題。然后拷貝兩份的解壓包,修改如下集群名要一致,不同,防止端口號沖突。修改完成后,依次啟動主節(jié)點兩個普通節(jié)點及插件,訪問,會顯示三個節(jié)點分布式配置完成 文章用來開啟Node和Elasticsearch學(xué)習(xí)的新篇章,持續(xù)更新中...來源:慕課網(wǎng)瓦力老師課程 http://www.imooc.com/learn/88...
閱讀 2983·2021-11-23 09:51
閱讀 1691·2021-10-15 09:39
閱讀 1086·2021-08-03 14:03
閱讀 2922·2019-08-30 15:53
閱讀 3462·2019-08-30 15:52
閱讀 2521·2019-08-29 16:17
閱讀 2834·2019-08-29 16:12
閱讀 1675·2019-08-29 15:26