Python 和 Ruby 也有這樣的框架,但因為在實際使用中會不可避免地用到含有同步代碼的庫,因此沒能成長起來,而在 Node.js 之前,JavaScript 的服務器端編程幾乎是空白,所以 Node.js 才得以建立起了一個所有 IO 均為異步的代碼庫。
大部分 Web 應用的瓶頸都在 IO, 即讀寫磁盤,讀寫網絡,讀寫數據庫。使用怎樣的策略等待這段時間,就成了改善性能的關鍵點。
PHP 的策略:多進程運行,直接原地等待 IO 完成。缺點:多個進程會消耗多份內存,進程間難以共享數據。
C/C++ 通常的策略:多線程運行,程序自己維護鎖的狀態。缺點:開發成本高,容易出錯,不易調試。
Python(Tornado): 多個請求在單個進程中輪流執行,遇到 IO 時切換到另一個請求。缺點:對於單個請求而言,依然沒有最高效地利用時間。
何謂「最高效地利用時間」?比如現在有兩個不相關的數據庫查詢,在 PHP 中通常會先執行一個,執行完成後再執行第二個(總時間是 a + b). 顯然這不是最高效的,應該同時執行兩個查詢,時間是 max(a, b).
Python 和其他支持多線程的語言的問題就在於,在語言層面,程序員很難告訴虛擬機,應當將兩個操作同時執行,即使有辦法,也相當麻煩,大多數人懶得去用(也不值得去用)。而因為 Node.js 喪心病狂地強制所有 IO 異步執行,Node.js 的程序員也可以說是輕車熟路,配合一些改善代碼可讀性庫(promise, async), 可以很輕松地讓不相干的操作並行執行。
上面講了異步 IO 的實現,那麼異步 IO 的優勢究竟體現在哪裡呢。實際上異步 IO 並不能神奇地減輕服務器的壓力,該加服務器還是一樣要加服務器,只不過異步 IO 會減少單個請求的時間,去掉單個請求中那些無意義的等待時間。所以單位時間內處理的請求沒有變化,但每個請求的處理時間卻減少了。從這個角度,服務器也節約了一些資源——即維持每個請求的連接消耗的內存。