理解 node.js 的時間循環
node.js 的第一個基本觀點是,I/O 操作是昂貴的:
目前的編程技術最大的浪費來自等待 I/O 操作的完成。有幾種方法可以解決這些對性能的影響:
同步:依次處理單個請求。
優點:簡單。
缺點:任何一個請求都會阻塞其余請求。
創建新進程:為每個請求創建一個進程處理
優點:容易。
缺點:擴展性不好,數百個連接意味著數百個進程。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 操作完成后執行。
來自:http://www.cnblogs.com/xxhuan/p/6071506.html