1. 爲何要異步I/O編程
(1)用戶體驗上:後端
併發的優點: M+N+... -> max(M,N,...) --> 使後端可以快速的響應資源數組
*併發的劣勢:...服務器
(2)資源分配:網絡
單線程同步編程模型會因阻塞I/O致使硬件資源得不到更優的使用。 -> 硬件使用效率低 -----多線程
| -----> Node:利用單線程,遠離多線程死鎖,狀態同步等問題;利用異步I/O,讓單線程遠離阻塞,以更好的使用CPU。併發
多線程編程模型會由於編程中的死鎖、狀態同步等問題讓開發人員頭疼。 -> 編程上的困難 -----異步
2. Node的異步I/O函數
(1)Node自身的執行模型 -- 事件循環(令回調函數十分廣泛)性能
當進程啓動時,Node會建立一個相似於while(true)的循環,沒執行一次循環體的過程稱爲一次"Tick"(「滴答」一聲)。
每一個Tick中,Node會:
A. 查看是否有事件待處理(什麼是事件?) 事件的來源:文件I/O,網絡請求
B. 若是有,取出事件以及相關的回調函數
C. 若是存在關聯的回調函數,就執行他們
D. 進入下一個循環
E. A -> 沒有事件了 -> 退出
誰來查看是否有事件? --> 觀察者(一個或多個)->文件I/O觀察者,網絡請求觀察者
產生事件 :文件I/O,網絡請求
觀察事件 :觀察者
取出並處理事件 :事件循環
(2)請求對象 -- 是異步I/O過程當中的重要中間產物,全部的狀態都保存在這個對象中,包括送入線程池等待執行以及I/O操做完畢後的回調問題。(仍是沒明白是什麼。)
(3)異步I/O的過程:
A.組裝好請求對象,送入I/O線程池等待執行
B.回調通知
回調函數的行爲:取出請求對象的result屬性做爲參數,取出oncomplete_sym屬性爲方法,而後調用執行,以此達到調用JavaScript中傳入的回調函數的目的。
事件循環,觀察者,請求對象,I/O線程池四者共同構成了Node異步I/O模型的基本要素。
3. 非I/O的異步API(什麼意思?)
(1)定時器
setTimeout() -- 單次定時執行任務
setInterval() -- 屢次定時執行任務
二者建立的定時器會被插入到定時器觀察者內部的一個紅黑樹種,每次Tick執行時,會從該紅黑樹中迭代取出定時器對象,檢查是否超過定時時間,若是超過,就造成一個事件,他的回調函數將當即執行。
存在的問題:時間是非精確
(2)process.nextTick()
每次調用process.nextTick()方法,只會講回調函數放入隊列中,在下一輪Tick時取出執行。相較於setTimeout(fn,0)更高效(無需調用紅黑樹)
(3)setImmediate()
功能相似於process.nextTick(),但優先級要低於前者。
具體實現上,process.nextTick()回調函數保存在一個數組中,setImmediate()的結果保存在鏈表中。
在行爲上,process.nextTick()在每輪循環中會將數組中的回調函數所有執行完,而setImmediate()在每輪循環中執行鏈表中的一個回調函數。
4. 事件驅動與高性能服務器
事件驅動的實質:經過主循環加事件觸發的方式運行程序。
幾種經典的服務器模型:
A. 同步式:一次只能處理一個請求,而且其他請求都處於等待狀態。
B. 每進程/每請求:爲每一個請求啓動一個進程,這樣能夠處理多個請求,可是他不具有拓展性,由於系統資源只有那麼多。
C. 每線程/每請求:爲每一個請求啓動一個線程來處理。儘管線程比進程要輕量,可是因爲每一個線程都佔用必定內存,當大併發請求到來時,內存將會很快用光,致使服務器變緩慢。(此方式的拓展性強於每進程/每請求,但對於大型站點而言依然不夠)【Apache使用】
【Node高性能的緣由之一】:Node經過事件驅動的方式處理請求,無須爲每個請求建立額外的對應線程,能夠省掉建立線程和銷燬線程的開銷,同時操做系統在調度任務時由於線程較少,上下文切換的代價很低。這使得服務器可以有條不紊的處理請求,即便在大量鏈接的狀況下,也不受線程上下文切換開銷的影響。