Node-1.高性能服務器

Node——高性能服務器

瀏覽器中JavaScript在單線程上執行,而且與UI渲染共享同一個線程,所以JavaScript在執行的時候UI渲染和響應是處於停滯狀態的。如果網頁獲取資源時同步獲取,那麼會阻塞頁面的其他操作,不能響應用戶的交互行爲,影響用戶體驗。

那Nodejs怎麼解決性能上的問題?基於事件的非阻塞異步I/O !
通過事件驅動節省爲每個請求創建額外線程和切換線程的開銷,使得服務器可以在大量連接的情況下也有條不紊地處理請求,非阻塞的設置讓它可以更好地提升響應吞吐,使Nodejs構建了一套完善的高性能異步I/O框架。

異步I/O與非阻塞I/O

說到Node的經常會聽到異步、非阻塞、回調、事件這些詞語。實際上,異步和非阻塞都達到了並行I/O的目的,但是從計算機內核I/O而言,異步/同步和阻塞/非阻塞實際上是兩回事。

操作系統內核對於I/O只有兩種方式:阻塞與非阻塞。

阻塞I/O: 系統內核調用後一定要等系統內核層面完成所有操作後,調用才結束。
阻塞I/O造成CPU等待I/O,浪費等待時間,CPU的處理能力不能得到充分利用。如讀取磁盤上的文件,系統內核在完成磁盤尋道、讀取數據、複製數據到內存後,這個調用才結束。

非阻塞I/O: 系統內核調用後會立即返回,可以使CPU得到重分利用,提高性能。

Nodejs的異步I/O

對於Node.js來說,利用單線程,遠離多線程死鎖、狀態同步等問題;利用異步I/O, 讓單線程遠離阻塞,可以更好利用CPU。

JavaScript發起調用到內核執行完I/O操作的過渡過程中,存在一種中間產物,它叫做請求對象請求對象 是異步I/O過程中的重要產物,所有的狀態都保存在這個對象中,包括送入線程池等到執行以及I/O操作完畢後的回調處理。

異步I/O
第一步:異步調用
JavaScript發起調用後,會將參數和回調方法組裝到請求對象上,由Node的核心模塊再去進行底層調用,送入I/O線程池等待執行,而JavaScript返回繼續往下執行代碼,這裏完成了異步調用的第一階段;
第二步:執行回調
線程池中的I/O操作調用完畢後會將結果存儲在請求對象中,然後將請求對象加入I/O觀察者的隊列中,作爲事件處理;I/O觀察者會取出請求對象中的回調函數及結果執行,從而達到調用JavaScript中傳入回調函數的目的。

這就是整個異步I/O的流程。

注意:JavaScript是單線程的,但是Node自身是多線程的,執行用戶代碼是一個線程,其他線程可以用於處理I/O等其他事務。

事件循環機制

JavaScript是單線程且非阻塞的腳本語言。JavaScript代碼在執行的時候只有一個主線程來處理所有任務。當遇到異步代碼的時候主線程不會阻塞停下來等待事件的回調執行,而是會按照事件循環機制執行。

什麼是事件循環機制?

當JavaScript執行處理到異步代碼的時候,會將事件加入事件隊列中,等待當前執行棧的任務執行完畢後,主線程再去事件隊列查看是否有任務。

異步任務有兩種類型:微任務(microtask)宏任務(macrotask)。不同類型的任務會放到不同的隊列中。
執行棧中所有任務執行完畢後,會先去檢查微任務隊列中是否有事件存在,如果有會依次執行微任務隊列中的回調,直到爲空;然後再去宏任務隊列中取出一個事件,把對應的回調放入執行棧中執行完畢後,檢查微任務列表中是否有事件存在,這樣無限循環執行事件,就叫做事件循環

事件循環是異步實現的核心。

Node與其他服務器比較

與其他Web服務器相比,

  • 比如Apache,採用的每線程/每請求,爲請求創建額外的對應線程,創建線程和銷燬線程需要額外的開銷,同時操作系統需要調度任務切爲線程換上下文,代價比較高;
  • Ruby的Event Machine,Python的Twisted,雖然採用了事件驅動,但是因爲同步I/O的存在,導致在事件循環中一旦I/O阻塞,會導致其餘的I/O無法立即執行,性能急劇下降,無法及時處理其他請求;
  • 對於Nginx也摒棄了多線程,採用了與Node相同的事件驅動,性能不錯,可以用於反向代理或負載均衡,不過Node處理具體業務能力更強,場景更廣,性能也不錯;

所以,Node使用異步I/O使I/O操作與CUP操作分離,而I/O操作的底層實現採用事件驅動,構建了完善的高性能異步I/O框架,使得Node可以構建高性能服務器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章