diff --git a/README.md b/README.md index f3af409..5e620ff 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Nodejs-Book 本書由NUTC IMAC團隊整理與製作,由於Node的特性有事件驅動與非同步執行等特性,並且非組塞模式I/O的特性,讓Node能夠在極低的資源底下提供高性能的負載能力,在現今的雲端平台Node也是不可或缺的角色之一。 - - + + ##參與貢獻 如果您想一起參與貢獻,可以參閱以下說明: @@ -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]`即可完成貢獻 diff --git a/cluster/README.md b/cluster/README.md new file mode 100644 index 0000000..f2400d6 --- /dev/null +++ b/cluster/README.md @@ -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 +}); +``` diff --git a/nodejs-V5110-Doc/Cluster/README.md b/nodejs-V5110-Doc/Cluster/README.md index 1f3df7c..4e376ce 100644 --- a/nodejs-V5110-Doc/Cluster/README.md +++ b/nodejs-V5110-Doc/Cluster/README.md @@ -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 退出代碼,如果正常退出 +- signal 的信號名稱"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 +類似cluster.on('listening'),但只能在worker底下工作 +```javascript +cluster.fork().on('listening', (address) => { + // Worker is listening +}); +``` +它不是從worker裡發出 ### Event: 'message' +- message +類似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() @@ -55,8 +235,41 @@ ### worker.send(message[, sendHandle][, callback]) ### worker.suicide # Event: 'disconnect' +- worker {Worker object} # Event: 'exit' +- worker +- code 退出代碼,如果正常退出 +- signal 的信號名稱"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 +當一個新的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' @@ -69,4 +282,4 @@ # cluster.settings # cluster.setupMaster([settings]) # cluster.worker -# cluster.workers \ No newline at end of file +# cluster.workers diff --git a/python-book b/python-book new file mode 160000 index 0000000..0a2cd21 --- /dev/null +++ b/python-book @@ -0,0 +1 @@ +Subproject commit 0a2cd219c058a43d0a0f05aecb44821dd257adaf