標籤:ima 其他 git 友好 方式 資料 簡單 影響 記憶體
node.js 的第一個基本觀點是,I/O 操作是昂貴的:
目前的編程技術最大的浪費來自等待 I/O 操作的完成。有幾種方法可以解決這些對效能的影響(來自 Sam Rushing):
同步:依次處理單個請求。
優點:簡單。
缺點:任何一個請求都會阻塞其餘請求。
建立新進程:為每個請求建立一個進程處理
優點:容易。
缺點:擴充性不好,數百個串連意味著數百個進程。fork()是 Unix 程式員的鎚子。因為它很有用,所有的問題都像是釘子。但這通常是多餘的。
線程:為每個請求建立一個線程處理。
優點:容易;由於線程的開銷通常都很小,相比於使用 fork 對核心更友好。
缺點:你的機器可能沒有線程,並且線程編程很容易變得複雜,也存在如何訪問共用資源的問題。
第二個基本觀點是,單線程串連非常消耗記憶體。
Apach 是多線程的:為每一個請求建立一個線程(或者進程,這取決於配置)。你可以看到增加當前串連數是如何消耗記憶體的,多個線程需要同時服務多個客戶。Nginx 和 Node.js 不是多線程的,因為多線程和多進程會帶來沉重的記憶體開銷。它們是單線程的,但是基於事件的。通過單線程處理多個串連,解決數千個線程/進程的開銷問題。
Node.js 為代碼保持著單線程的運行環境
Node.js 確實是單線程啟動並執行:你不能執行任何並發代碼;例如“sleep”,這會使伺服器停止。
當代碼運行時,node.js不會響應用戶端的其他請求,因為它只有一個線程在執行代碼。或者你可以使用一些 CPU-密集型代碼,例如,調整圖片尺寸,這仍然會阻塞其他請求。
然而,一切代碼都能並存執行
並沒有辦法讓代碼在單線程中並行運行。除了所有的 I/O 操作和非同步事件,以下代碼並不會阻塞伺服器:[codesyntax lang="javascript"]
在一個請求中執行以上代碼,資料庫在休眠時其他請求也能被很好的處理。
這樣有什麼好處?我們什麼時候應該將同步改為非同步/並存執行?
同步執行是好方法,因為這使代碼編寫變得簡單(與多線程相比,並發問題導致了 WTFs)。
在 node.js 中,你不需要擔心後台會發生什麼:只需要使用回調執行 I/O 操作;這保證了你的代碼不會被中斷,同時 I/O 操作不會阻塞其他請求,每個請求也不會增加線程/進程的開銷(例如,Apache 中的記憶體開銷)。
非同步 I/O 操作也是好方法,因為 I/O 操作相較於大多數代碼的執行更昂貴,我們應該做其他的事情,而不是等待 I/O 操作
時間迴圈是“一個能夠加工和處理外來事件並將它們轉換為回調調用的實體”。因此 I/O 調用的關鍵在於 Node.js 能夠從一個請求切換到另一個請求。在一個 I/O 調用中,代碼會儲存回呼函數,並將控制權返回給 node.js 的運行時環境。當資料可用時回呼函數將被調用。
當然,在後台中,有用於資料庫訪問和執行進程的線程和進程。然而,這並沒有使代碼暴露,因此你不需要為 I/O 操作擔心,例如,資料庫或者其他進程對於每一個請求都是非同步,這些線程的執行結果會通過事件迴圈返回給代碼。與 Apache 模式相比,不需要為每個串連提供單個線程,因此需要更少的線程和線程開銷;只有當真的需要並行運行時,即使管理權在 Node.js 也能夠運行。
除了 I/O 操作的調用,Node.js 希望其他的所有請求都能迅速響應;例如:CPU-密集型工作應該被拆分到互動事件的進程中,或者像 WebWorkers 那樣抽象的使用。(顯然地)這意味著在後台沒有其他的線程並發運行互動事件。基本上,所有的監聽事件對象(都是 EventEmitter 的執行個體)都支援非同步互動事件,你能夠以這種方式與阻塞代碼互動,例如使用 files,sockets 或者子進程,這些在 Node.js 中都是 EventEmitters。[多核][8]也可以使用這種方法,請參見:node-http-proxy
內部實現
在內部,node.js 依賴於 libev 實現事件迴圈,以 libeio 為輔助,使用混合線程實現非同步 I/O 操作。要想學習更多,就需要查看 libev 的文檔。
如何在 Node.js 中使用非同步?
Tim Caswell 在他出色的演講中描述了這種模式:
First-class 函數。例如,我們將函數作為參數傳遞,在需要的時候執行他們。
Function 形式。也被稱作匿名函數或者閉包函數,當 I/0 操作完成後執行。
原文:Understanding the node.js event loop
理解 node.js 的事件迴圈