張雨萌
(上海浦東發(fā)展銀行總行信息科技部,上海 200000)
本文首先重新定義一個(gè)名詞——服務(wù)。服務(wù)特指由網(wǎng)絡(luò)實(shí)現(xiàn)的應(yīng)用系統(tǒng)間的API 調(diào)用與響應(yīng)過程,其中API調(diào)用和響應(yīng)不局限于特定形式。
一個(gè)服務(wù)存在兩個(gè)重要角色:服務(wù)調(diào)用方和服務(wù)提供方。本文將服務(wù)中發(fā)起API 調(diào)用的應(yīng)用系統(tǒng)稱之為服務(wù)調(diào)用方,將提供API 調(diào)用并作出響應(yīng)的應(yīng)用系統(tǒng)稱之為服務(wù)提供方。
在網(wǎng)絡(luò)架構(gòu)中,存在著客戶端、服務(wù)器之分,與之對應(yīng)存在著客戶端應(yīng)用系統(tǒng)、服務(wù)器應(yīng)用系統(tǒng)。需要強(qiáng)調(diào)的是,服務(wù)器應(yīng)用系統(tǒng)一定是服務(wù)提供方,客戶端應(yīng)用系統(tǒng)一定是服務(wù)調(diào)用方。
在服務(wù)A 中,如果服務(wù)器應(yīng)用系統(tǒng)在收到服務(wù)調(diào)用方的API 調(diào)用請求后,需要與其他服務(wù)提供方新建另一個(gè)服務(wù)B,并根據(jù)新建服務(wù)B 的響應(yīng)結(jié)果完成服務(wù)A。該服務(wù)器應(yīng)用系統(tǒng)同時(shí)具有服務(wù)提供方和服務(wù)調(diào)用方兩個(gè)角色,服務(wù)A 被稱為依賴型服務(wù)。如果服務(wù)器應(yīng)用系統(tǒng)在收到服務(wù)調(diào)用方的API 調(diào)用請求后,不需要新建服務(wù),依靠本系統(tǒng)即可完成服務(wù)的響應(yīng),則該服務(wù)器應(yīng)用系統(tǒng)僅作為服務(wù)提供方角色,本文將該服務(wù)稱之為獨(dú)立型服務(wù)。
因承載服務(wù)類型的不同,本文將獨(dú)立型服務(wù)中的服務(wù)器應(yīng)用系統(tǒng)稱為完全服務(wù)器應(yīng)用系統(tǒng)。將依賴型服務(wù)中的服務(wù)器應(yīng)用系統(tǒng)稱為服務(wù)器—客戶端應(yīng)用系統(tǒng)。
服務(wù)器—客戶端應(yīng)用系統(tǒng)的本質(zhì)依然是服務(wù)器應(yīng)用系統(tǒng),其與完全服務(wù)器應(yīng)用系統(tǒng)主要差異表現(xiàn)在服務(wù)中所處位置不同、提供服務(wù)的基本過程不同。
完全服務(wù)器應(yīng)用系統(tǒng)和服務(wù)器—客戶端應(yīng)用系統(tǒng)在服務(wù)中所處位置如圖1 所示,完全服務(wù)器應(yīng)用系統(tǒng)在服務(wù)中是I/O 請求數(shù)據(jù)的終點(diǎn)和I/O 響應(yīng)數(shù)據(jù)的起點(diǎn),而服務(wù)器—客戶端應(yīng)用系統(tǒng)只是I/O 數(shù)據(jù)流轉(zhuǎn)的中間環(huán)節(jié)。
Fig.1 Position of total-server application system and server-client application system in the service圖1 完全服務(wù)器應(yīng)用系統(tǒng)和服務(wù)器—客戶端應(yīng)用系統(tǒng)在服務(wù)中的位置
完全服務(wù)器應(yīng)用系統(tǒng)在服務(wù)中的基本處理流程如圖2所示,服務(wù)器—客戶端應(yīng)用系統(tǒng)在服務(wù)中處理基本過程如圖3 所示。通過對比發(fā)現(xiàn),服務(wù)器—客戶端應(yīng)用系統(tǒng)與完全服務(wù)器應(yīng)用系統(tǒng)的最大差異在于其將業(yè)務(wù)處理過程分成兩個(gè)階段,并在業(yè)務(wù)處理過程中新建另一個(gè)服務(wù),其業(yè)務(wù)處理結(jié)果依賴于新建服務(wù)的響應(yīng)數(shù)據(jù)。
Fig.2 Basic process of total-server application system in the service圖2 完全服務(wù)器應(yīng)用系統(tǒng)在服務(wù)中的基本處理流程
Fig.3 Basic process of server-client application system in the service圖3 服務(wù)器—客戶端應(yīng)用系統(tǒng)在服務(wù)中的基本處理流程
服務(wù)器應(yīng)用系統(tǒng)的線程模型研究較多。1999 年,為了優(yōu)化網(wǎng)絡(luò)編程的程序設(shè)計(jì),Schmid[1]在應(yīng)用架構(gòu)設(shè)計(jì)層面提出了Reactor 模型,并結(jié)合Unix 網(wǎng)絡(luò)編程技術(shù)[2]給出模型實(shí)現(xiàn);針對突破單機(jī)性能局限提升應(yīng)用系統(tǒng)的服務(wù)承載能力,Dan[3]提出了c10k 問題,并首次提出包括NIO 在內(nèi)的解決方案;Doug[4]在總結(jié)應(yīng)用程序線程模型基礎(chǔ)上,結(jié)合Java NIO 技術(shù)進(jìn)一步細(xì)化了Reactor 模型,提出單線程Reactor模型、線程池Reactor 模型以及多Reactor 模型。以上學(xué)者的核心思想是提高應(yīng)用系統(tǒng)的服務(wù)承載能力,解耦I(lǐng)/O 事件和進(jìn)程/線程資源、復(fù)用進(jìn)程/線程資源以及合理的程序模型設(shè)計(jì)。在上述研究基礎(chǔ)上,各類基于Java NIO[5-7]以及基于Reactor 模型的應(yīng)用設(shè)計(jì)[8-21]得到廣泛應(yīng)用。
受限于當(dāng)時(shí)的業(yè)務(wù)場景,關(guān)于服務(wù)器應(yīng)用系統(tǒng)的線程模型設(shè)計(jì)多關(guān)注于解耦在服務(wù)提供方角色下的I/O 事件和進(jìn)程/線程資源,并未考慮在服務(wù)調(diào)用方角色下I/O 事件和進(jìn)程/線程資源的解耦方式,即相關(guān)行業(yè)實(shí)踐和研究更適配于完全服務(wù)器應(yīng)用系統(tǒng)而無法適配服務(wù)器—客戶端應(yīng)用系統(tǒng)。
為適配服務(wù)器—客戶端應(yīng)用系統(tǒng),本文提出拓展的多Reactor 模型,在多Reactor 模型基礎(chǔ)上增加異步回調(diào)處理機(jī)制,同時(shí)解耦服務(wù)調(diào)用方和服務(wù)提供方兩個(gè)角色上的I/O事件與線程資源。
經(jīng)典線程模型如圖4 所示。服務(wù)器應(yīng)用系統(tǒng)在收到客戶端應(yīng)用系統(tǒng)的API 調(diào)用請求后,為每個(gè)請求分配一個(gè)handler 線程,完成socket 對象的建立和業(yè)務(wù)處理,在handler 線程完成業(yè)務(wù)處理后,通過已建立的socket 對象向客戶端應(yīng)用系統(tǒng)作出響應(yīng)。
Fig.4 Classic thread model圖4 經(jīng)典線程模型
經(jīng)典線程模型存在如下缺點(diǎn):①經(jīng)典線程模型下,服務(wù)器應(yīng)用系統(tǒng)承載服務(wù)的能力有限,其并發(fā)服務(wù)數(shù)受限于可用線程數(shù);②在大量handler 線程阻塞的情況下,服務(wù)器應(yīng)用系統(tǒng)會出現(xiàn)線程頻繁切換、CPU 使用率增高等問題,導(dǎo)致系統(tǒng)處理能力下降。
為了優(yōu)化經(jīng)典線程模型,提出以I/O 多路復(fù)用技術(shù)為基礎(chǔ)的Reactor 模型并應(yīng)用于服務(wù)器系統(tǒng)中。
單線程Reactor 模型首次將服務(wù)器應(yīng)用系統(tǒng)在服務(wù)提供方角色下的I/O 事件與線程資源進(jìn)行解耦,該模型如圖5所示。
Fig.5 Basic Reactor model圖5 單線程Reactor 模型
單線程Reactor 模型主要包括Reactor Thread、Acceptor以及負(fù)責(zé)業(yè)務(wù)處理的Handler 線程。
1.2.1 單線程Reactor 模型模塊介紹
Reactor Thread 保存了服務(wù)器創(chuàng)建網(wǎng)絡(luò)連接的必要信息以及一個(gè)I/O 多路復(fù)用器,在創(chuàng)建ServerSocket 對象后將ServerSocket 對象注冊到I/O 多路復(fù)用器,其不斷監(jiān)聽socket對象的可讀(READABLE)、可寫(WRITABLE)事件。Reactor Thread 包括一個(gè)Dispatcher 組件,在有可讀(READABLE)的I/O 數(shù)據(jù)時(shí),Dispatcher 組件將創(chuàng)建一個(gè)負(fù)責(zé)業(yè)務(wù)處理的Handler 線程。
Acceptor 通過Reactor Thread 中的I/O 多路復(fù)用器獲取可建立連接(ACCEPTABLE)狀態(tài)的I/O 請求,并創(chuàng)建一個(gè)已連接狀態(tài)(ACCEPTED)的socket 對象,將該socket 對象注冊到Reactor Thread 中的I/O 多路復(fù)用器。
Handler 線程用于具體業(yè)務(wù)處理,其由Dispatcher 組件創(chuàng)建,并在業(yè)務(wù)處理過程完成后銷毀。
1.2.2 單線程Reactor 模型不足
從架構(gòu)層面看,一個(gè)Reactor Thread 在面對高負(fù)載、多并發(fā)應(yīng)用場景時(shí),單線程Reactor 模型存在如下問題:
(1)性能問題。Reactor Thread 承擔(dān)職責(zé)太多,一個(gè)Reactor Thread 同時(shí)處理上百萬個(gè)通訊鏈路,性能上無法支撐;另外,由于業(yè)務(wù)處理線程無法重用,其每次創(chuàng)建的成本必然在高并發(fā)時(shí)對系統(tǒng)運(yùn)行造成巨大壓力。
(2)可靠性問題。一旦Reactor Thread 出現(xiàn)故障,會導(dǎo)致整個(gè)系統(tǒng)通信模塊不可用,不能接收和處理I/O 數(shù)據(jù),造成節(jié)點(diǎn)故障。
雖然單線程Reactor 模型首次把服務(wù)器應(yīng)用系統(tǒng)在服務(wù)提供方角色下的I/O 事件與線程資源進(jìn)行了解耦,但由于業(yè)務(wù)處理線程無法重用,其每次創(chuàng)建的成本必然在高并發(fā)時(shí)對系統(tǒng)運(yùn)行造成巨大壓力。為改善單線程Reactor 模型業(yè)務(wù)處理線程無法重用的問題,線程池Reactor 模型應(yīng)運(yùn)而生。線程池Reactor 模型如圖6 所示。
相較于單線程Reactor 模型,線程池Reactor 模型去除Dispatcher 組件,使用了線程池進(jìn)行業(yè)務(wù)處理,此舉可以避免業(yè)務(wù)處理線程不斷創(chuàng)建、銷毀。但線程池Reactor 模型仍然沒有解決單線程Reactor 模型中Reactor Thread 同時(shí)處理上百萬個(gè)通訊鏈路時(shí)的性能及可靠性問題。
Fig.6 Thread pool Reactor model圖6 線程池Reactor 模型
作為目前主流的線程模型,多Reactor 模型成功解決了線程池Reactor 模型的性能及可靠性問題,其核心思想是將ServerSocket 對象的創(chuàng)建同socket 對象可讀(READABLE)、可寫(WRITABLE)事件的監(jiān)聽解耦,并將監(jiān)聽socket 對象可讀(READABLE)、可寫(WRITABLE)事件的工作放到多個(gè)線程、多個(gè)I/O 多路復(fù)用器中。
多Reactor 模型結(jié)構(gòu)如圖7 所示,主要包括Main Reactor、Acceptor、Sub Reactor、Handler Thread Pool 四部分。
Fig.7 Multiple Reactor model圖7 多Reactor 模型
1.4.1 多Reactor 模型模塊介紹
(1)Main Reactor。保存服務(wù)器創(chuàng)建網(wǎng)絡(luò)連接的必要信息并創(chuàng)建ServerSocket 對象,將ServerSocket 對象注冊到其與Acceptor 共享的一個(gè)I/O 多路復(fù)用器中,注冊監(jiān)聽的I/O事件為可建立連接(ACCEPTABLE)狀態(tài)的I/O 請求,一般由單線程完成。
(2)Acceptor。用于監(jiān)聽服務(wù)調(diào)用方的網(wǎng)絡(luò)連接請求,通過其與Main Reactor 共享的I/O 多路復(fù)用器獲取Server-Socket 對象,可建立連接(ACCEPTABLE)狀態(tài)的I/O 請求,創(chuàng)建一個(gè)已連接狀態(tài)(ACCEPTED)的socket 對象,并將該socket 對象注冊到Sub Reactor 中的某個(gè)I/O 多路復(fù)用器中。
(3)Sub Reactor。不斷監(jiān)聽socket 對象的可讀(READABLE)、可寫(WRITABLE)事件,讀取可讀狀態(tài)下socket 對象的I/O 數(shù)據(jù),并調(diào)用Handler Thread Pool 中的線程進(jìn)行業(yè)務(wù)處理;一旦有可寫狀態(tài)的I/O 數(shù)據(jù)就將I/O 數(shù)據(jù)返回給客戶端應(yīng)用系統(tǒng)。采用線程池方式構(gòu)建,線程池中的每個(gè)線程保存一個(gè)I/O 多路復(fù)用器。
(4)Handler Thread Pool。負(fù)責(zé)具體的業(yè)務(wù)處理,輸入為Sub Reactor 讀取的socket 對象I/O 數(shù)據(jù),輸出為Sub Reactor 響 應(yīng) 的I/O 數(shù) 據(jù)。
1.4.2 無法適配問題
多Reactor 模型通過對模塊的合理規(guī)劃,解耦了Server-Socket 對象的創(chuàng)建,以及socket 對象可讀(READABLE)、可寫(WRITABLE)事件的監(jiān)聽,有效提升了服務(wù)器應(yīng)用系統(tǒng)的承載能力和可靠性。
但由于在多Reactor 模型中,Handler Thread Pool 線程只用于業(yè)務(wù)處理,未考慮到依賴型服務(wù)的業(yè)務(wù)處理過程,這意味著使用多Reactor 模型的服務(wù)器—客戶端應(yīng)用系統(tǒng)在作為服務(wù)調(diào)用方時(shí),在其他服務(wù)提供方的響應(yīng)時(shí)間內(nèi)業(yè)務(wù)處理線程是阻塞狀態(tài),無法承載其他服務(wù)。而如果阻塞線程過多,新服務(wù)請求的響應(yīng)速度或已積壓服務(wù)請求的處理速度將大幅降低,造成服務(wù)大量超時(shí),所以多Reactor 模型有著天然無法適配服務(wù)器—客戶端應(yīng)用系統(tǒng)的問題。從這個(gè)角度看,多Reactor 模型只是一個(gè)適配完全服務(wù)器應(yīng)用系統(tǒng)的線程模型。
為解耦服務(wù)器—客戶端應(yīng)用系統(tǒng)在服務(wù)調(diào)用方和服務(wù)提供方兩個(gè)角色上的I/O 事件與線程資源,本文在多Reactor 模型基礎(chǔ)上提出了一種拓展多Reactor 模型,旨在適配服務(wù)器—客戶端應(yīng)用系統(tǒng)。
拓展多Reactor 模型如圖8 所示。其中,Main Reactor 的作用與多Reactor 模型中的Main Reactor 一致,保存服務(wù)器創(chuàng)建網(wǎng)絡(luò)連接的必要信息并創(chuàng)建ServerSocket 對象,將ServerSocket 對象注冊到其與Acceptor 共享的一個(gè)I/O 多路復(fù)用器中。注冊監(jiān)聽的I/O 事件為可建立連接(ACCEPTABLE)狀態(tài)的I/O 請求,由單線程完成。
Fig.8 Extended multi-Reactor mode圖8 拓展多Reactor 模型
Acceptor 用于監(jiān)聽服務(wù)調(diào)用方的網(wǎng)絡(luò)連接請求,通過其與Main Reactor 共享的I/O 多路復(fù)用器獲取ServerSocket對象,建立連接(ACCEPTABLE)狀態(tài)的I/O 請求,創(chuàng)建一個(gè)已連接狀態(tài)(ACCEPTED)的socket 對象,并將該socket 對象注冊到1st Sub Reactor 中的某個(gè)I/O 多路復(fù)用器中。
1st Sub Reactor 不斷監(jiān)聽socket 對象的I/O 可讀(READABLE)、可寫(WRITABLE)事件,讀取可讀狀態(tài)下socket 對象的I/O 數(shù)據(jù),同時(shí)調(diào)用1st Thread Pool 中的線程進(jìn)行前半程業(yè)務(wù)處理,一旦有可寫狀態(tài)的I/O 數(shù)據(jù)就將I/O 數(shù)據(jù)返回給客戶端應(yīng)用系統(tǒng)。采用線程池構(gòu)建,線程池中的每個(gè)線程保存一個(gè)I/O 多路復(fù)用器。
1st Thread Pool 負(fù)責(zé)執(zhí)行前半程業(yè)務(wù)處理過程,通過2nd Sub Reactor 與其他服務(wù)提供方建立連接并發(fā)送I/O 請求數(shù)據(jù)。
2nd Sub Reactor 是實(shí)現(xiàn)服務(wù)調(diào)用方角色上解耦I(lǐng)/O 事件與線程資源的核心模塊,主要承載服務(wù)器—客戶端應(yīng)用系統(tǒng)新建服務(wù)的工作,在其內(nèi)部同樣保持一個(gè)或多個(gè)I/O多路復(fù)用器,在獲取到其他服務(wù)提供方的I/O 響應(yīng)數(shù)據(jù)后,通過2nd Thread Pool 中的線程回調(diào)后半程的業(yè)務(wù)處理過程。
2nd Thread Pool 負(fù)責(zé)執(zhí)行后半程的業(yè)務(wù)處理過程,輸入為2nd Sub Reactor 獲取到的I/O 響應(yīng)數(shù)據(jù)。在完成后半程業(yè)務(wù)處理后,2nd Thread Pool 中的線程通過修改1st Sub Reactor 的I/O 多路復(fù)用器中的socket 監(jiān)聽事件為可寫(WRITABLE),1st Sub Reactor 作為服務(wù)提供方角色下的響應(yīng)數(shù)據(jù)返回給客戶端應(yīng)用系統(tǒng)。
拓展多Reactor 模型中線程資源、I/O 事件的時(shí)序圖如圖9 所示,該模型在服務(wù)調(diào)用方和服務(wù)提供方兩個(gè)角色上均實(shí)現(xiàn)了I/O 事件與線程資源的解耦。
本文對拓展多Reactor 模型下的服務(wù)器—客戶端應(yīng)用系統(tǒng)和多Reactor 模型下的服務(wù)器—客戶端應(yīng)用系統(tǒng)分別進(jìn)行HTTP 協(xié)議的性能測試,并比較了服務(wù)器—客戶端應(yīng)用系統(tǒng)在兩個(gè)模型下的性能測試結(jié)果。
性能測試按照圖1 所示的鏈路開展,其中客戶端應(yīng)用程序使用Apache Jmeter 性能測試工具,在15min 內(nèi),通過向服務(wù)器—客戶端應(yīng)用系統(tǒng)不斷發(fā)送HTTP 請求以獲取服務(wù)器—客戶端應(yīng)用系統(tǒng)的最高TPS 和交易失敗率,其獲取響應(yīng)數(shù)據(jù)的超時(shí)時(shí)間為20s。完全服務(wù)器使用多Reactor 模型,業(yè)務(wù)處理過程不涉及I/O 操作,其服務(wù)響應(yīng)時(shí)間設(shè)置為10s,系統(tǒng)可用線程數(shù)設(shè)置為150。
Fig.9 Life cycle of thread resources and I/O events in the extended multi-reactor model圖9 拓展多Reactor 模型中線程資源、I/O 事件的生命周期
服務(wù)器—客戶端應(yīng)用系統(tǒng)在拓展多Reactor 模型和多Reactor 模型下都使用Apache Tomcat 作為Web 容器,通過開啟Http11NioProtocol 協(xié)議實(shí)現(xiàn)多Reactor 模型。在此基礎(chǔ)上,拓展多Reactor 模型下的服務(wù)器—客戶端應(yīng)用系統(tǒng)借助Servlet 3.1 中的異步處理技術(shù)以及HttpAsyncClient 工具進(jìn)行構(gòu)建。
服務(wù)器—客戶端應(yīng)用系統(tǒng)在拓展多Reactor 模型和多Reactor 模型下使用工具如表1 所示。
Table 1 Comparison of tools used in server-client application system under extended multi-reactor model and multiple reactor model表1 服務(wù)器—客戶端應(yīng)用系統(tǒng)在拓展多Reactor 模型和多Reactor 模型下使用工具對比
性能測試結(jié)果如表2 所示。
通過性能測試結(jié)果可以發(fā)現(xiàn),在多Reactor 模型下,服務(wù)器—客戶端應(yīng)用系統(tǒng)最高的TPS 一直未超過50,且在200 和500 并發(fā)用戶數(shù)時(shí),分別出現(xiàn)16.78%和96.26 失敗率,究其原因是由于服務(wù)器—客戶端應(yīng)用系統(tǒng)的業(yè)務(wù)處理線程大量阻塞,沒有足夠的線程資源處理已經(jīng)積壓的請求。在拓展多Reactor 模型下,服務(wù)器—客戶端應(yīng)用系統(tǒng)最高的TPS 可以突破50,在500 并發(fā)用戶數(shù)時(shí),最高TPS 可以到達(dá)200,且失敗率一直保持為0%。
Table 2 Performance test results of server-client application system under extended multi-reactor model and multiple reactor model表2 服務(wù)器—客戶端應(yīng)用系統(tǒng)在拓展多Reactor 模型和多Reactor 模型下的性能測試結(jié)果
實(shí)驗(yàn)證明,拓展多Reactor 模型在正確率、TPS 兩個(gè)方面均優(yōu)于多Reactor 模型。
本文首次從場景上將服務(wù)器應(yīng)用系統(tǒng)細(xì)分為完全服務(wù)器應(yīng)用系統(tǒng)和服務(wù)器—客戶端應(yīng)用系統(tǒng),并分析了兩種應(yīng)用系統(tǒng)的差異之處,為系統(tǒng)設(shè)計(jì)提供了一種新的視角;本文還針對性地提出了拓展多Reactor 模型的線程設(shè)計(jì),為服務(wù)器—客戶端應(yīng)用系統(tǒng)進(jìn)一步提升服務(wù)承載能力提供了一種新的可行模型。
本文仍有不足之處,如模型的構(gòu)建基于Java 語言,在Apache Tomcat 以及HttpAsyncClient 工具的支持下完成,而生產(chǎn)實(shí)際中存在多種語言和工具,如何構(gòu)建一個(gè)輕量級的Java 工具庫,以及基于其他語言構(gòu)建工具庫是下一步研究的方向。