Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Nodejs-Book
本書由NUTC IMAC團隊整理與製作,由於Node的特性有事件驅動與非同步執行等特性,並且非組塞模式I/O的特性,讓Node能夠在極低的資源底下提供高性能的負載能力,在現今的雲端平台Node也是不可或缺的角色之一。



##參與貢獻

如果您想一起參與貢獻,可以參閱以下說明:
Expand All @@ -18,10 +18,10 @@

- 點選 Github [nodejs-book](https://github.com/imac-iot/nodejs-book) 並將其fork到自己的專案底下
- 在命令列輸入 git clone https://github.com/「USER_NAME」/nodejs-book
- 在修改完成後
- 在修改完成後
- $ git add --all
- $ git commit -m "Fix issue 「Your Message」"
- $ git push
- $ git push
- 當做完上述步驟時再對 [nodejs-book](https://github.com/imac-iot/nodejs-book) 提出 `[new pull request]`即可完成貢獻


Expand Down
90 changes: 90 additions & 0 deletions cluster/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
##Cluster
一個運行在Nodejs的單個線程中,為了充份利用多核心系統,使用者有時候要啟動Nodejs的cluster來處理負擔

cluster模組允許使用者容易的去創造child processes去共享server ports

```javascript
const cluster = require('cluster'); //引入cluster函式庫
const http = require('http'); //引入http函式庫
const numCPUs = require('os').cpus().length; //引入os CPU 數量
//cluster.isMaster可以判斷目前是在主程式還是在fork出去的child process
if (cluster.isMaster) { //在主程式
// Fork workers. //
for (var i = 0; i < (numCPUs-1); i++) { //numCPUs-1是為了讓有一顆CPU可以執行系統分配或運算
cluster.fork(); //利用cluster.fork()產生worker
} //
//
console.log('核心數:'+numCPUs); //核心數
//
cluster.on('death', function(worker) { //
console.log('worker ' + worker.pid + ' died'); //
}); //
} else { //是child process,就直接執行伺服器程式
// Worker processes have a http server. //
http.Server(function(req, res) { //
res.writeHead(200); //
res.end("hello world\n"); //印出hello world
}).listen(8000); //port8000
} //

```
執行Nodejs 127.0.0.1:8000

```不知到是什麼
$ NODE_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 online
```
請注意,視窗,目前還不可能建立一個名為pipe server 的工人

##如何工作
worker processes使用child_process.fork()方法所產生,以便他們可以和 parent via IPC 溝通,並透過是伺服器處理往返
cluster模組支援分發和傳入兩個方法
- 第一個(而再所有平台上默認除了windows),是round-robin 方法,其中master process在port做監聽,接受新的連接並在round-robin方式下分發他們橫越每個workers,一些內建的智慧會避免worker processes超載工作
- 第二個途徑,是master process建立一個listen socket,並將發送給有興趣的workers,然後workers直接接受傳入的連接
第二個方法理論上應該的到最好的性能,然而再實踐上,分佈往往是非常不平衡的,由於操作系統的調度變幻莫測,負載經觀察,超過70%的連接在two processes 就結束了

因為server.listen()處理了大部分的master process工作,有三種情況,其中一般的Nodejs和cluster處理行為是不同的

- 1.server.listen({fd: 7})在master和worker通信過程,通過傳遞文件,master會監聽"文件描述為7",而不是傳遞"文件描述為7"的引用
- 2.server.listen(handle)master和worker溝通過程,透過handle函數進行溝通
- 3.server.listen(0)在master和worker溝通過程,cluster中的worker會打開一個隨機窗口,透過socket通信

###cluster的各種屬性和函數
- cluster.setttings:配置cluster參數對象
- cluster.isMaster:判斷是不是master結點
- cluster.isWorker:判斷是不是worker結點
- Event: 'fork': 監聽創建worker過程事件
- Event: 'online': 監聽worker創建成功事件
- Event: 'listening': 監聽worker向master狀態事件
- Event: 'disconnect': 監聽worker中斷事件
- Event: 'exit': 監聽worker退出事件
- Event: 'setup': 監聽更新Master事件
- cluster.setupMaster([settings]): 設置cluster参数
- cluster.fork([env]): 創建worker進程
- cluster.disconnect([callback]): 關閉worket進程
- cluster.worker: 獲得當前的worker對象
- cluster.workers: 獲得cluster中所有存活的worker對象

###worker的各種屬性和函數:可以透過cluster.workers, cluster.worket獲得
-worker.id: id編號
-worker.process: ChildProcess
-worker.suicide: 在disconnect()後,判断worker是否已刪除
-worker.send(message, [sendHandle]): master给worker發送訊息。注:worker對master發送訊息要process.send(message)
-worker.kill([signal='SIGTERM']): 刪除指定的worker
-worker.disconnect(): 中斷worker連接,讓worker消失
-Event: 'message': 監聽master和worker的message事件
-Event: 'online': 監聽指定的worker創建成功事件
-Event: 'listening': 監聽master向worker狀態事件
-Event: 'disconnect': 監聽worker中斷事件
-Event: 'exit': 監聽worker退出事件

###Event: 'disconnect'
類似cluster.on('disconnect')事件
```shell
cluster.fork().on('disconnect', () => {
// Worker has disconnected
});
```
215 changes: 214 additions & 1 deletion nodejs-V5110-Doc/Cluster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,195 @@


# Cluster
一個運行在Nodejs的單個線程中,為了充份利用多核心系統,使用者有時候要啟動Nodejs的cluster來處理負擔

cluster模組允許使用者容易的去創造child processes去共享server ports

```javascript
const cluster = require('cluster'); //引入cluster函式庫
const http = require('http'); //引入http函式庫
const numCPUs = require('os').cpus().length; //引入os CPU 數量
//cluster.isMaster可以判斷目前是在主程式還是在fork出去的child process
if (cluster.isMaster) { //在主程式
// Fork workers. //
for (var i = 0; i < (numCPUs-1); i++) { //numCPUs-1是為了讓有一顆CPU可以執行系統分配或運算
cluster.fork(); //利用cluster.fork()產生worker
} //
//
//
cluster.on('death', function(worker) { //
console.log('worker ' + worker.pid + ' died'); //
}); //
} else { //是child process,就直接執行伺服器程式
// Worker processes have a http server. //
http.Server(function(req, res) { //
res.writeHead(200); //
res.end("hello world\n"); //印出hello world
}).listen(8000); //port8000
} //

```
執行Nodejs 127.0.0.1:8000

```不知到是什麼
$ NODE_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 online
```
請注意,視窗,目前還不可能建立一個名為pipe server 的工人
# How It Works
worker processes使用child_process.fork()方法所產生,以便他們可以和 parent via IPC 溝通,並透過是伺服器處理往返
cluster模組支援分發和傳入兩個方法
- 第一個(而在所有平台上默認除了windows),是round-robin 方法,其中master process使用port做監聽,接受新的連接並在round-robin方式下分發他們橫越每個workers,一些內建的智慧會避免worker processes超載工作
- 第二個途徑,是master process建立一個listen socket,並將發送給有興趣的workers,然後workers直接接受傳入的連接
第二個方法理論上應該的到最好的性能,然而再實踐上,分佈往往是非常不平衡的,由於操作系統的調度變幻莫測,負載經觀察,超過70%的連接在two processes 就結束了

因為server.listen()處理了大部分的master process工作,有三種情況,其中一般的Nodejs和cluster處理行為是不同的

- 1.server.listen({fd: 7})在master和worker溝通過程,透過傳遞文件,master會監聽"文件描述為7",而不是傳遞"文件描述為7"的引用
- 2.server.listen(handle)master和worker溝通過程,透過handle函數進行溝通
- 3.server.listen(0)在master和worker溝通過程,cluster中的worker會打開一個隨機窗口,透過socket通信

在Nodejs中沒有路由邏輯,或者在你的程式中每個workers沒有共享狀態,因此這對設計程式非常重,不會依賴太多的內存記憶體像是sessions and login

因為每個workers在程序中式分離的,workers可以依照程式需求關閉或重起,並不會互相影響。只要有worker繼續存活,服務將繼續連接。如果全部worker被中斷,以存在的連接將被中斷,新的連接將被拒絕。Nodejs不會自動管理workers數,管理worker pool是你的管理責任
# Class: Worker
一個Worker物件包含全部的公共和方法,在master裡它可以用cluster.workers獲得,在worker裡它可以用cluster.worker獲得
### Event: 'disconnect'
類似cluster.on('disconnect')事件,但只能在worker底下工作
```javascript
cluster.fork().on('disconnect', () => {
// Worker has disconnected
});
```
### Event: 'error'
這個事件和之前提過的一樣child_process.fork()
在worker也可使用process.on('error')
### Event: 'exit'
監聽worker退出事件
- code <Number>退出代碼,如果正常退出
- signal <String>的信號名稱"SIGHUP",表示該處理被關閉
類似cluster.on('exit')事件,但只能在worker底下工作
```javascript
const worker = cluster.fork();
worker.on('exit', (code, signal) => {
if( signal ) {
console.log(`worker was killed by signal: ${signal}`);
} else if( code !== 0 ) {
console.log(`worker exited with error code: ${code}`);
} else {
console.log('worker success!');
}
});
```
### Event: 'listening'
監聽worker向master狀態事件
- address <Object>
類似cluster.on('listening'),但只能在worker底下工作
```javascript
cluster.fork().on('listening', (address) => {
// Worker is listening
});
```
它不是從worker裡發出
### Event: 'message'
- message <Object>
類似cluster.on('message'),但只能在worker底下工作
這個事件與之前的child_process.fork()相似
在worker裡也可使用process.on('message')
舉個例子,這裡是一個cluster,持續送出計數要求,在master處理使用系統訊息
```javascript
const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {

// Keep track of http requests
var numReqs = 0;
setInterval(() => {
console.log('numReqs =', numReqs);
}, 1000);

// Count requests
function messageHandler(msg) {
if (msg.cmd && msg.cmd == 'notifyRequest') {
numReqs += 1;
}
}

// Start workers and listen for messages containing notifyRequest
const numCPUs = require('os').cpus().length;
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}

Object.keys(cluster.workers).forEach((id) => {
cluster.workers[id].on('message', messageHandler);
});

} else {

// Worker processes have a http server.
http.Server((req, res) => {
res.writeHead(200);
res.end('hello world\n');

// notify master about the request
process.send({ cmd: 'notifyRequest' });
}).listen(8000);
}
```
### Event: 'online'
監聽worker創建成功事件
類似cluster.on('online'),但只能在worker底下工作
```javascript
cluster.fork().on('online', () => {
// Worker is online
});
```
它不是從worker裡發出
### worker.disconnect()
- 中斷worker連接,讓worker消失
- 在worker裡,這個功能將會關閉所有服務,等待服務上的"close"事件,然後斷開IPC通道
- 在master裡,內部訊號發送到worker,造成在自己本身呼叫.disconnect()使得.exitedAfterDisconnect被設定
- 注意,一個服務器被關閉後,它將不再接受新的連接,但連接可以由任何其他聆聽worker接受。
- 現有連接照樣將被允許關閉,當沒有更多的連接存在,將看到server.close(),worker的IPC通道,將允許它關閉優雅的結束。
- 以上僅適用於服務器連接,客戶端連接不會自動關閉worker,和斷開再退出之前不會等待他們關閉
- 注意在一個worker裡,process.disconnect存在,但是它沒有功能,它是disconnect,因為長期工作的伺服器連接可能從disconnecting阻止worker,它可能發送一個有用的訊息,所以特定動作可用來關閉它們。它可以被使用在超時上,如果一段時間後disconnect事件上未發出,中斷worker
```javascript
if (cluster.isMaster) {
var worker = cluster.fork();
var timeout;

worker.on('listening', (address) => {
worker.send('shutdown');
worker.disconnect();
timeout = setTimeout(() => {
worker.kill();
}, 2000);
});

worker.on('disconnect', () => {
clearTimeout(timeout);
});

} else if (cluster.isWorker) {
const net = require('net');
var server = net.createServer((socket) => {
// connections never end
});

server.listen(8000);

process.on('message', (msg) => {
if(msg === 'shutdown') {
// initiate graceful close of any connections to server
}
});
}
```
### worker.exitedAfterDisconnect
### worker.id
### worker.isConnected()
Expand All @@ -55,8 +235,41 @@
### worker.send(message[, sendHandle][, callback])
### worker.suicide
# Event: 'disconnect'
- worker {Worker object}
# Event: 'exit'
- worker <cluster.Worker>
- code <Number>退出代碼,如果正常退出
- signal <String>的信號名稱"SIGHUP",表示該處理被關閉
當有任何workers被結束時,cluster模組會發送"exit"事件
透過再次使用fork()函數,可以使用這個事件來重起worker
```javascript
cluster.on('exit', (worker, code, signal) => {
console.log('worker %d died (%s). restarting...',
worker.process.pid, signal || code);
cluster.fork();
});
```
看child_process event: 'exit'
# Event: 'fork'
- worker <cluster.Worker>
當一個新的worker被分支出來,cluster模組會產生一個"fork"事件。這被用來紀錄worker進行過的活動,以及建立你自己的超時判斷
```javascript
var timeouts = [];
function errorMsg() {
console.error('Something must be wrong with the connection ...');
}

cluster.on('fork', (worker) => {
timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', (worker, address) => {
clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', (worker, code, signal) => {
clearTimeout(timeouts[worker.id]);
errorMsg();
});
```
# Event: 'listening'
# Event: 'message'
# Event: 'online'
Expand All @@ -69,4 +282,4 @@
# cluster.settings
# cluster.setupMaster([settings])
# cluster.worker
# cluster.workers
# cluster.workers
1 change: 1 addition & 0 deletions python-book
Submodule python-book added at 0a2cd2