阿里技術文章:淺談 Node.js 和 PHP 進程管理

來源:互聯網
上載者:User
所周知,PHP 佔據了服務端程式設計語言的半壁江山,正如汪峰在音樂圈的地位一般。隨著 Node.js 逐漸走上服務端編程的舞台,關於 PHP 和 Node.js 孰優孰劣的爭論也不曾間斷。

壟斷性的市場份額足以佐證 PHP 的優秀。並且 HHVM 虛擬機器、PHP 7 的革新,也給 PHP 帶來了跨越式的效能突破。然而,當我們為語言層面的效能差異喋喋不休時,卻往往忽略了 Web 模型在效能表現中的權重。

從 CGI 到 FastCGI

早期的 Web 服務,是基於傳統的 CGI 協議實現的。每個發送到伺服器的請求,都需要經過啟動進程、處理請求、結束進程三個步驟,以至於訪問量增大時,系統資源(如記憶體、CPU 等)開銷也巨大,導致伺服器效能下降甚至服務中斷。

圖 1:簡單的 CGI 流程示意

在 CGI 協議下,解析器的反覆載入是效能低下的主要原因。如果讓解析器進程長駐記憶體,那麼它只需啟動一次,就可以一直執行著,不必每次都重新 fork 進程,這就有了後來的 FastCGI 協議。

如果 FastCGI 僅僅做到這樣,那麼和 Node.js 單進程單線程的模型是基本一致的:Node.js 進程啟動後保持持續運行,所有的請求都由這個進程接收和處理,當某個請求引起未知錯誤時,才可能致使進程退出。

事實上 FastCGI 並沒有那麼簡單,為了保證服務的穩定性,他被設計成了多進程調度的模式:

圖 2:Nginx + FastCGI 執行過程

這個過程同樣可以描述為三個步驟:

  • 首先,初始化 FastCGI 進程管理器,並啟動多個 CGI 解譯器子進程;
  • 接著,當請求到達 Web 服務器時,進程管理器選擇並串連一個子進程,將環境變數和標準輸入發送給它,處理完成後將標準輸出和錯誤資訊返還給 Web 服務器;
  • 最終,子進程關閉串連,繼續等待下一個請求的到來;

從 child_process 到 cluster

我們回過頭來看看 Node.js 的進程管理方式。

原生 Node.js 的單進程單執行緒模式是一個極易被噴的槽點。這種機制也決定了 Node.js 天生只支援單核 CPU,無法有效地利用多核資源,一旦進程崩潰,還會導致整個 Web 服務的土崩瓦解。

圖 3:簡單的 Node.js 的請求模型

和 CGI 一樣,單一進程始終面臨著可靠性低、穩定性差的問題,當真正服務於生產環境時,這樣的弱點相當致命。如果代碼本身足夠健壯,倒可以在一定程度上避免出錯,但同時也對測試工作提出了更高要求。現實中我們無法避免代碼 100% 不出紕漏,有些東西容易編寫測試案例,有些東西卻只能依靠人肉目測。

所幸 Node.js 提供了 child_process 模組,通過簡單 fork 即可隨意建立出子進程。如果為每個 CPU 分別指派一個子進程,多核利用就完美實現了。於此同時,由於 child_process 模組本身繼承自 EventEmitter 這個基礎類,事件驅動使得進程間的通訊非常高效。

圖 4:簡單的 Node.js master-worker 模型(扒的淘傑老濕的圖)

為了簡化龐雜的父子進程模型實現,Node.js 緊接著又封裝了 cluster 模組,不論是負載平衡、資源回收,還是進程守護,它都會像保姆一樣幫你默默地搞定一切。具體技術細節可以參考淘傑老濕的 《當我們談論 cluster 時我們在談論什麼(上)》 和 《當我們談論 cluster 時我們在談論什麼(下)》 ,裡面有所有關於 cluster 方案的推演和實現,這裡不再贅述。

在 Node.js 裡,要讓應用跑在多核叢集上,只需寥寥幾行代碼就萬事大吉了:

var cluster = require('cluster');var os = require('os');if (cluster.isMaster) {    for (var i = 0, n = os.cpus().length; i < n; i ++) {        cluster.fork();    }} else {    // 啟動應用...}

那麼反觀 FastCGI 協議,它又是如何處理這種模型的呢?

PHP-FPM 的天生缺陷

PHP-FPM 是 PHP 針對 FastCGI 協議的具體實現,也是 PHP 在多種伺服器端應用編程連接埠(SAPI:cgi、fast-cgi、cli、isapi、apache)裡使用最普遍、效能最佳的一款進程管理器。它同樣實現了類似 Node.js 的父子進程管理模型,確保了 Web 服務的可靠性和高效能。

PHP-FPM 這種模型是非常典型的多進程同步模型,意味著一個請求對應一個進程線程,並且 IO 是同步阻塞的。所以儘管 PHP-FPM 維護著獨立的 CGI 進程池、系統也可以很輕鬆的管理進程的生命週期,但註定無法像 Node.js 那樣,一個進程就可以承擔巨大的請求壓力。

受制於伺服器的硬體設施,PHP-FPM 需要指定合理的 php-fpm.conf 配置:

pm.max_children # 子進程最大數pm.start_servers # 啟動時的子進程數pm.min_spare_servers # 最小空閑進程數,空閑進程不夠時自動補充pm.max_spare_servers # 最大空閑進程數,空閑進程超過時自動清理pm.max_requests = 1000 # 子進程請求數閾值,超過後自動回收

和 JS 不一樣的是,PHP 進程本身並不存在記憶體泄露的問題,每個進程完成請求處理後會回收記憶體,但是並不會釋放給作業系統,這就導致大量記憶體被 PHP-FPM 佔用而無法釋放,請求量升高時效能驟降。

所以 PHP-FPM 需要控制單個子進程請求次數的閾值。很多人會誤以為 max_requests 控制了進程的並發串連數,實際上 PHP-FPM 模式下的進程是單一線程的,請求無法並發。這個參數的真正意義是提供請求計數器的功能,超過閾值數目後自動回收,緩解記憶體壓力。

或許你已經發現了問題的關鍵:儘管 PHP-FPM 架構卓越,但還是卡在單一進程的效能上了。

Node.js 天生沒有這個問題,而 PHP-FPM 卻無法保證,它的穩定性受制於硬體設施和設定檔的契合度,以及 Web 服務器(通常是 Nginx)對 PHP-FPM 服務的負載調度能力。

ReactPHP,事件驅動,非同步執行,非阻塞 IO

對 PHP 7 的狂熱掩蓋了 Node.js 帶來的猛烈衝擊。當大家還沉醉在如何選擇 HHVM 還是 PHP 7 的時候,ReactPHP 也在茁壯成長,它徹徹底底拋棄了 nginx + php-fpm 的傳統架構,轉而模仿並接納了 Node.js 的事件驅動和非阻塞 IO 模型,甚至連副標題,都起得一毛一樣:

Event-driven, non-blocking I/O with PHP.

鑒於大家都比較瞭解 Node.js,對 ReactPHP 的原理就不再贅述了,我們可以認為它就是個 PHP 版的 Node.js。拿它和傳統架構(Nginx + PHP-FPM,公平起見,PHP-FPM 只開一個進程)去做對比,結果是這樣的:

圖 5:輸出“Hello World”時的 QPS 曲線

圖 6:查詢 SQL 時的 QPS 曲線

我們可以看到,當事件驅動、非同步執行、非阻塞 IO 被移植嫁接到 PHP 上後,即便沒了 PHP-FPM 支撐,QPS 曲線依然不錯,在 IO 密集型的情境下,效能甚至得到了成倍成倍的提升。

事件和非同步回調機制真是太贊了,它巧妙地將大規模並發、大輸送量時的擁堵化解為一個非同步事件隊列,然後挨個解決阻塞(如檔案讀取,資料庫查詢等)。

針對單進程模型的吐槽,或許有些偏激。不過顯而易見的事實是,單進程模型的可靠性,在 Web 服務器和進程管理器層面是有很大的最佳化空間的,而高並發的處理能力取決於語言特性,說白了就是事件和非同步支援。

這兩點想必是讓 Node.js 天生驕傲的事情,但在 PHP 裡沒有得到原生支援,只能通過類比步進操作的方式來支援類似 Node.js 的事件機制,所以 ReactPHP 其實也並沒有想象中那麼完美。

結束語

大部分時候,當我們比較語言優劣,容易局限在語言本身,而忽視了配套的一些關鍵因素。

就拿 PHP 來說,這兩年聽到了太多關於即時編譯器(JIT)、opcode 緩衝、抽象文法樹(AST)、HHVM 等等之類的話題。當這些最佳化逐步完備,語言層面的問題,早已不再是 Web 效能的短板了。如果實在不行,我們還可以把複雜任務交給 C 和 C++,以 Node.js addon 或者 PHP 擴充的形式,輕輕鬆鬆就搞定了。

都說 PHP 是“世界上最好的語言”,既然如此,也是時候學習下 Node.js 事件驅動和非同步回調,考慮考慮如何對 PHP-FPM 進行大刀闊斧的革新。畢竟不管是 Node.js 還是 PHP,我們所擅長的地方,終將還是 Web,高效能的 Web。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.