用 Node.js 實作多個 Process 監聽並處理同一個 Port
Node.js 實作了一個內建的模組『cluster』,目的在於提供開發者一個容易的方式,針對多核心機器進行支援。可惜的是,截至今天 Node.js v0.6.14 版本,『cluster』功能還是不夠完整,從官方文件上可以得知,其 API 仍處於實驗(Experimental)階段,在日後會有極大的變動(Drastic changes in future versions)。
而就 git repository 上最新的 Node.js 開發版本來看,『cluster』模組已經大幅度更改,也從簡單的 function call 變成了物件的設計。所以,目前非常不建議在自家產品中大量使用 『cluster』,至少短期內,他都不是穩定可用的,會有很大的改動空間。
不過,你如果是要實作 Web Service,會想要使用『cluster』的目的就顯而易見:『透過多個 Process 分擔流量和工作量』。若只是單單要做到這一點,我們可以自己來實作。
通常我們有兩種方式達成這個需求:
前者的做法,是建立一個常駐程式或機器,統一處理所有連線的需求,然後將這些連線做流量平衡(Load Balance),轉送並分配給後端多個程序或機器處理,待處理完成後,再將後端的回應送回給使用者。這樣的 Proxy 機制,你可以用已經相當成熟的解決方案達成,像是 Nginx 或 Apache。亦或是自己使用 Node.js 撰寫,但由於這不是本文的重點,日後再來討論這個實作的細節。
而後者,使用多個程序(Process) 監聽並處理同一個 Port,是本文的主題,也是『cluster』模組正在做的事。如果你有看過『cluster』的程式碼,就會發現它其實主要運用了兩個技術:『取得 net._handle』和『利用 process.send() 傳送 Handle 給子行程(child process)』。
知道了關鍵後,實作上就不是什麼大問題,主要流程是:
主程式的範例如下:
worker.js 的程式:
如此,就可以讓所有子行程共同監聽 80 port,透過瀏覽器來看,你應該可以隨機看到『Worker 1』、『Worker 2』、『Worker 3』或『Worker 4』的字樣,分屬不同 Process 回傳的網頁內容。
後記
能這樣使用的原因是,在 Node.js 中,『http』是繼承『net』的衍生實作,所以他們的底層 handle 可以通用。此外,此做法不限於 HTTP Server,也可以在用於一般 TCP Server。
差點忘了提到,Express Web Framework 這類的框架,都是繼承『http』,所以都可以使用相同的做法。
而就 git repository 上最新的 Node.js 開發版本來看,『cluster』模組已經大幅度更改,也從簡單的 function call 變成了物件的設計。所以,目前非常不建議在自家產品中大量使用 『cluster』,至少短期內,他都不是穩定可用的,會有很大的改動空間。
不過,你如果是要實作 Web Service,會想要使用『cluster』的目的就顯而易見:『透過多個 Process 分擔流量和工作量』。若只是單單要做到這一點,我們可以自己來實作。
通常我們有兩種方式達成這個需求:
- 反向代理(Reverse Proxy) 技術
- 使用多個程序(Process) 監聽並處理同一個 Port
前者的做法,是建立一個常駐程式或機器,統一處理所有連線的需求,然後將這些連線做流量平衡(Load Balance),轉送並分配給後端多個程序或機器處理,待處理完成後,再將後端的回應送回給使用者。這樣的 Proxy 機制,你可以用已經相當成熟的解決方案達成,像是 Nginx 或 Apache。亦或是自己使用 Node.js 撰寫,但由於這不是本文的重點,日後再來討論這個實作的細節。
而後者,使用多個程序(Process) 監聽並處理同一個 Port,是本文的主題,也是『cluster』模組正在做的事。如果你有看過『cluster』的程式碼,就會發現它其實主要運用了兩個技術:『取得 net._handle』和『利用 process.send() 傳送 Handle 給子行程(child process)』。
知道了關鍵後,實作上就不是什麼大問題,主要流程是:
- 運用『net』建立一個 Server,目的在於監聽(listen) 80 Port
- 建立多個子行程(child process) 準備處理連線
- 將『net』的內建 Handle 傳給子行程(child process)
- 在子行程(child process) 接收主程序傳來的 Handle
- 在建立 HTTP Server 時,直接使用主程序傳來的 Handle
- 關閉只用來初使化 Handle 的 Server
主程式的範例如下:
var child_process = require('child_process'); var net = require('net'); var tcpSrv = net.createServer(); tcpSrv.listen(80, function() { for (var i = 1; i <= 4; i++) { var worker = child_process.fork('worker.js'); worker.send(i, tcpSrv._handle); } tcpSrv.close(); });
worker.js 的程式:
var http = require('http'); process.on('message', function(id, handle) { http.createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Worker ' + id); }).listen(handle); });
如此,就可以讓所有子行程共同監聽 80 port,透過瀏覽器來看,你應該可以隨機看到『Worker 1』、『Worker 2』、『Worker 3』或『Worker 4』的字樣,分屬不同 Process 回傳的網頁內容。
後記
能這樣使用的原因是,在 Node.js 中,『http』是繼承『net』的衍生實作,所以他們的底層 handle 可以通用。此外,此做法不限於 HTTP Server,也可以在用於一般 TCP Server。
差點忘了提到,Express Web Framework 這類的框架,都是繼承『http』,所以都可以使用相同的做法。
請問可以轉貼您的文章到我的網站嗎?
回覆刪除http://i-me.tw/
可以的 :-)
回覆刪除感謝!文章網址: http://i-me.tw/post/2012/04/715
回覆刪除如有任何不妥,煩請指正。
Sam