袁文光
(長沙商貿旅游職業技術學院 網絡中心, 長沙 410116)
高校校園Web 應用服務平臺承載了學校眾多業務系統,日常承擔著師生海量在線并發訪問請求。師生線上業務辦理、信息查詢、文件上傳下載等均屬網絡、磁盤I/O 密集型操作。師生線上活躍程度越高,平臺的網絡和磁盤I/O 承載壓力越大,給Web 系統帶來的負擔越重[1],往往導致訪問速度下降、響應時間過長、請求失敗,甚至出現系統崩潰等問題。隨著高校的辦學規模的擴大和教育資源的豐富,Web 應用平臺的服務能力逐漸不能滿足學校發展需求。傳統的解決方法大多是通過橫向擴展提升平臺的并發和數據處理能力,然而單純的軟硬件數量的擴充并不能從根本上解決問題,因此,需通過縱向擴展法提升服務器單機系統的I/O 能力。本文擬提出一種基于Node.js 的Web 應用架構,為解決校園Web 高并發性能問題提供一種新方法。
Node.js 最早衍生于RyanDahl 的一個Web 項目,其將V8 引擎從Chrome 瀏覽器中剝離出來,并做二次封裝[2],使V8 引擎在服務器端狀態更佳,突破了JavaScript 只能在瀏覽器環境中運行的局限。在后端Web 應用的開發中,JavaScript 不再依賴瀏覽器的解析,可隨意訪問本地文件,直接操作數據庫[3]。由于JavaScript 異步、非阻塞I/O的特性,Node.js 不斷完善的功能及豐富的第三方庫,使其在高并發的應用場景中優勢凸顯。Node.js擁有兩項核心技術:一是事件驅動、單線程;二是異步、非阻塞I/O。
Node.js 可將用戶請求抽象成事件,按照事件機制處理用戶的并發請求,并用線程循環監測事件。當監測到事件發生時,將該事件添加到事件隊列中,線程不必等待事件處理完成,即可持續監測和觸發新事件。在單線程模式下,可以同時處理海量請求,避開了多線程模式下系統需要不斷創建、銷毀線程及在線程間切換管理所需的開銷和復雜性,使Node.js 對系統內存的依賴程度更低,消耗的系統I/O 資源更少。
Web 系統是典型的I/O 密集型應用系統,Web 應用性能瓶頸主要在于I/O 操作,包括磁盤I/O(連接數、機器性能等)、緩存服務、網絡I/O等。在異步I/O 方式下,遇到I/O(磁盤/網絡)操作時,線程不會被阻塞掛起,也無須等待操作結果,可另行處理其他請求,大大提高事務處理效率。在同步I/O 方式下,要同時處理多個請求,必須開啟多個線程,需耗費大量的系統內存資源。
當前,各高校主流的Web 服務器如IIS、Apache、Tomcat 等,都是以多線程機制應對高并發請求[4]。譬如Apache+PHP,雖然PHP 是單線程語言,但Apache 為多線程機制,一個外部請求觸發一個Apache 線程,同時開啟一個PHP 線程。以創建一個線程消耗2 M 內存計算,標配16 G 內存的服務器,理論上也只能創建8 000 個線程。當線程數超過線程池上限時,服務器響應請求的數量會驟減。因此,多線程框架可以處理的用戶請求數量,受限于服務器物理內存的大小。同時,系統在多線程模式下,進行線程頻繁開啟、銷毀、上下文切換、狀態同步等操作時,需額外消耗CPU、I/O 資源,使系統資源緊張加劇。
校園 Web 系統的編程語言(JAVA,ASP.net等)基本是多線程、同步、阻塞I/O 機制[5],在與用戶建立連接時,每個連接都創建一個線程。N 個連接,服務器上就有N 個線程。線程在執行I/O 操作時,經常會被阻塞,需在整個I/O 操作完成后才能繼續執行。譬如:傳統服務器(Apache、Tomcat)采用了I/O 阻塞機制,每條數據寫入數據庫都要等待一段時間(等上一條寫完再寫下一條),導致I/O占用的資源無法及時釋放,后續任務需持續等待,降低了系統資源利用率。
針對高校Web 系統的性能問題,傳統的解決方法主要是增加服務器數量,搭建服務器集群及數據庫集群等。增加服務器數量的橫向擴展法,雖能暫時提高并發能力,但多線程架構易導致平臺硬件資源緊張,同步、阻塞I/O 會導致服務器資源利用率降低,建設成本高昂等問題。因此,從單機系統出發,修改、優化、強化系統架構,實施縱向擴展法,提升服務器并發能力,非常必要。
Node.js 具有事件驅動、異步I/O 等特性,能滿足高校高并發Web 應用需求。結合Node.js 技術特性,通過構建新架構、多服務器技術集成等可提升服務器單機性能。架構設計的重點是提升系統應對密集型磁盤I/O 操作的能力,其分層架構如圖1 所示。

圖1 Web 應用架構設計
架構縱向分為服務、緩存、數據、消息隊列4層。每一層承擔相應職責,分工又合作,共同構建高效系統。
(1)服務層縱向分割為負載均衡層、業務邏輯層、基礎服務層。負載均衡層實現負載均衡功能,接收HTTP 請求,將靜態、動態請求分別分發至靜態資源服務器和Node.js 服務器。業務邏輯層主要處理業務邏輯,調用基礎服務層處理用戶請求,并將結果返回用戶瀏覽器;使用局域NPM(Node Package Manage)包服務,將業務邏輯層的各功能縱向拆分;實施分布式部署,且各功能獨立成模塊并分別部署為一個微服務,使用消息隊列、Rest 請求等,保持各微服務間的通信,可充分調用高速緩存,將高頻數據的讀寫從數據庫遷移至內存,降低磁盤I/O 頻次?;A服務層實現文件存儲、權限管理、日志記錄、異常處理等功能,為業務邏輯層提供基礎支撐。
(2)緩存層縱向分割為緩存訪問層和緩存更新層。緩存訪問層為上層提供緩存服務,訪問和更新緩存;緩存更新層實現系統緩存更新,接收隊列信息更新RabbitmQ 的消息。
(3)數據層縱向分割為數據訪問層和數據存儲層。數據層調用Node.js 的數據接口,訪問和更新數據庫,數據存儲層可以是關系數據庫(MySQL、SQLserver) 或者 NoSQL 數據庫(HBase、MongoDB)。
(4)消息隊列層可降低系統耦合度,增加系統內聚性,構成異構系統,實現服務器間或業務間的消息接收和發送(如數據庫更新、用戶注銷、緩存更新等)延緩耗時的I/O 操作,使系統具備更好的擴展性和維護性。
如圖2 所示,基于Node.js 的Web 應用架構實現技術主要包括負載均衡、Cluster 多核、功能模塊化、高速緩存、數據庫、RabbitmQ 消息隊列等技術。

圖2 高并發架構的實現技術
(1)負載均衡技術
負載均衡層處于架構的第一層,是網絡用戶請求的開始。使用HAProxy 編制路由規則,通過負載均衡算法,將請求分為動態和靜態兩類,將靜態請求直接路由至靜態資源服務器,將動態請求分發至不同的業務服務器,以平衡各服務器的負載。HAProxy 適合負載較大的Web 應用,能支持海量并發連接,同時,HAProxy 具備可視化的管理端,實時監控服務器負荷,配合人工調控,可達到最佳效果。
(2)Cluster 多核技術
Node.js 默認單進程單線程模式,默認模式下的Node.js 只能使用服務器多核CPU 中的一核,造成資源浪費。為了充分利用多核CPU 的計算資源,Node.js 內置Cluster 模塊,以創建多個進程,由多個進程共同提供服務。Cluster 多核技術發揮了多核CPU 的優勢,可以很好地用于集群式部署。
(3)功能模塊化技術
作為主流前端開發語言,JavaScript 的顯著缺點是缺乏完善的模塊機制,必須使用<Script>#標簽來引用文件。Node.js 制訂了CommonJS 規范,完善了JavaScript 語言,使其在前后端都能運行。Node.js 的包管理器 NPM 是基于 CommonJS 的Package 規范實現的。
局域NPM 倉庫實現業務功能模塊化,如圖3所示,局域NPM 倉庫系統按照業務邏輯進行功能拆分,分解成相互獨立的模塊,模塊之間通過接口調用。

圖3 局域NPM 倉庫設計
(4)高速緩存技術
在高并發環境下,Web 系統性能瓶頸集中于磁盤I/O。因此,在Web 服務器與數據庫間設計加入Redis 緩存數據庫,以降低磁盤I/O 頻次。Redis是內存數據庫,數據直接寫在內存,也直接從內存中讀取,避免直接讀寫磁盤。具體而言,對于實時性高的數據杜絕用戶直接訪問磁盤數據庫(DB),而是訪問緩存數據庫,降低磁盤I/O 頻次。如圖4所示,Redis 實現緩存中數據的讀取和更新,同時監聽消息隊列并更新緩存,保證數據的一致性。

圖4 高速緩存實現技術
(5)數據庫技術
在Web 系統中,數據的讀操作遠多于數據的寫操作,一個寫操作尚未完成,讀操作將無法進行。在高并發背景下,為解決用戶的讀和寫操作的沖突,設計主從數據庫,以實現讀寫分離,即主數據庫進行寫操作,從數據庫進行讀操作。采用Keepalive 雙機熱備技術保證主從數據庫中內容的一致性和安全性,可有效減少數據讀寫的時間,其設計如圖5 所示。

圖5 Keepalive 雙機熱備
圖 5 中,Master 數據庫、Slave 數據庫互為主從,但master 只進行寫操作,slave 只進行讀操作。主從數據庫通過keepalive 做成高可用,當master出問題,由slave 接替master 工作,即讀寫都在slave 操作。當master 恢復正常,master 自動同步故障時間段數據,接替slave 的寫操作。
(6)RabbitmQ 消息隊列技術
RabbitmQ 消息隊列技術優點有:延緩業務邏輯執行,緩解應用的負載壓力;解耦應用,各模塊間更加獨立,提高了開發效率;構筑異構應用,可使用不同技術實現內部應用。
對所構建的基于Node.js 的Web 應用架構進行壓力測試,主要測試響應時間、響應率和吞吐量等并發性能指標。響應時間指用戶從開始發起請求到接收返回信息所耗用的時間,一般取平均值;響應率代表系統在規定時間內,成功處理的請求占總請求的百分比;吞吐量則表示系統在1 s 內最大能處理請求的數量。
上述并發性能指標需通過系統關鍵路徑進行測試驗證。系統關鍵路徑由系統外部響應、CPU計算、I/O 操作等構成。Web 系統用戶的注冊過程包含外部響應、CPU 校驗、數據庫I/O 讀寫等環節,因此,選擇Web 用戶注冊這條關鍵路徑進行架構指標壓力測試。
基于控制變量的方法,分別搭建多核+Node.js和Apache+J2ee 兩種不同架構的Web 應用,編制相同的業務邏輯和數據庫,并部署于硬件完全相同的2 臺服務器上,配置為操作系統CentOS,CPU型號為Intel(R)Xeon(R)CPU E5-2430@2.20 GHz,物理核數為6,線程數量為12,內存16 G,安置在同一局域網內,以降低網絡傳輸的不穩定可能帶來的偏差。使用Apache 自帶工具,對兩種不同架構搭建的Web 應用進行性能測試并加以比較。兩種架構實驗條件配置如表1 所示。

表1 Node.js 和Apache 實驗條件配置
對兩種架構搭建的Web 應用的關鍵路徑(用戶注冊模塊接口)進行壓力測試,在模擬高并發場景下分別向這兩臺服務器發送測試請求(10 次到20 萬次不等),比較兩種不同架構的服務器響應時間、響應率和吞吐量的變化,結果如圖6、7、8。
從圖6 可以看出,當并發數小于1 500 時,Node.js 和Apache 的平均響應時間差距很小。隨著并發數的增大,Node.js 與Apache 的平均響應時間差距拉大,Node.js 的響應時間顯著低于Apache,表明在面對高并發問題時,Node.js 的處理更高效。

圖6 平均響應時間比較
由圖7 可知,當每秒并發數小于4 500 時,Node.js 和Apache 請求響應率基本持平,隨著并發量的增大,Node.js 請求響應優勢明顯。

圖7 請求響應率比較
由圖8 可見,隨著并發數增大,Node.js 和Apache 的吞吐量均有所降低,Apache 不斷創建和銷毀線程的開銷,線程間頻繁切換的開銷,制約了其高并發處理能力,而Node.js 則具有更高的吞吐量。

圖8 吞吐量對比
本文提出了一種基于Node.js 的高并發Web應用架構,通過縱向擴展法提升服務器單機并發性能。與傳統的多線程、同步阻塞I/O 架構相比,面對海量用戶請求,使用Node.js 和多服務器技術集成構建的Web 服務器,處理更高效,優勢更明顯,可大幅提升Web 應用服務平臺的處理效率,改善用戶體驗,能為高校校園網業務提供更優質的服務平臺。