最近在寫一個基於XE2 DATASNAP的中間層,以前也寫過基於DCOM的MIDAS中間層,看到網上一些同仁對中間層的提問。我摘錄倆個典型的提問,如下:
第一個提問:
Midas三層程序,如何支持大量用戶同時請求連接?
系統結構: TClientDataSet -> TSocketServer -> scktsrvr.exe -> RemoteDataModule -> ADO
要求:允許500-1000個客戶端同時“請求”連接,服務器和客戶端均不能死掉。
現狀:在現有的程序中,只要同時“請求”連接的客戶端達到一個較大的數量,例如80或100,應用服務器就停止響應了。客戶端嘗試連接時,不但無法建立連接,甚至都無法獲得
一個“返回錯誤”,而是跟着一起停止響應。
期望:能夠允許500-1000個客戶端同時發出連接請求、嘗試連接。當然,可以限制並發工作數,哪怕限制到很小的數量;但是,超出這個數量的客戶端在執行
TSocketConnection.Open時,必須能立刻收到一個“連接數已滿,拒絕連接”的返回信息;或者進入一個等待隊列,重試指定次數或者重試指定時間;或者,哪怕僅僅拋出個異常
。但不允許停止響應,不允許無聲無息地死掉。
第二個提問:
基於Midas/Socket/Scktsrvr的三層程序,如何在連接環節限制連接數?
架構:TSocketConnection -> scktsrvr.exe -> RemoteDataModule
現狀:大量客戶端(如200個)同時發起連接請求,中間層或scktsrvr明明無法處理這么多的連接,卻又不推辭,最終導致客戶端、中間層和scktsrvr都死掉。
期望:讓scktsrvr限制Socket的數量,超過限度(如20個)之后的客戶端可以向scktsrvr發出連接請求,但scktsrvr不分配給Socket,拒絕連接;客戶端超時之后,彈出對話框提示用戶。
網上關於中間層的提問看似千奇百怪,其實集中起來,無非就是上面的倆個大問題(雖然上面的提問都是針對MIDAS的,但同樣適合於DATASNAP):
1。讓中間層支持大量並發而不會死掉;
2.控制最大的連接數量,防止服務器因硬件資源被耗完而死掉。其實這倆個問題也可以說是等同一個問題。
這倆個問題可以解決嗎?答案是能。
首先描述一下許多同仁寫中間層的方法:
1. 中間層線程服務方式選擇“SESSION”,這也是MIDAS/DATASNAP默認的線程方式,大家往往跟着默認;
2. 在遠程數據模塊上放一些諸如數據庫連接控件、數據集控件。。。。。。
無可否定,這樣寫中間層是最容易,也是許多教科書中介紹的方式。但正因為這樣才導致了上面的問題。
因為這樣的方式對服務器端的資源消耗是重量級的:當一個客戶端連接上來,服務端對應開一個線程為其服務,直至客戶端終止連接才關閉這個線程。為什么說是重量級的呢?當線程開啟后,會創建遠程數據模塊,然后遠程數據模塊里面的一大堆數據集對象、數據連接對象都會被創建,如此多的對象在客戶端的連接周期內一直存在,且只能為一個客戶連接提供服務。因為服務器的資源消耗是重量級的,一旦客戶連接數量稍微一多,服務器的硬件資源很可能馬上就消耗完。此時如果又沒有做最大連接數量上限的控制,就只能任由服務器因資源耗盡而當機。這就是這種方式寫中間層的弊端。即這種方式決定了中間層能夠支持的客戶端並發數量是非常有限的,因為每一個客戶端對服務端的資源占用都是重量級的:一是占用時間長(客戶端只要還保持着連接,服務端資源就一直被占着),二是服務端要創建一大堆對象給每一個客戶連接服務。
如何解決?
1. 化重量級對象占用為輕量級占用;2. 化長時間占用為實時占用。
於是對象池呼之欲出,對象池中的對象們能夠為所有的客戶連接服務,再不是某些對象資源只能為某一個客戶連接服務。
最關鍵的是改變服務端的線程服務方式:不能用默認的"session"線程方式,要改成線程池的方式。對於操作系統來說,能夠創建線程的數量總是有限的,比如WINDOWS大概能夠創建2000個左右的空線程對象。改為了線程池的方式以后,線程數量再不是限制。
改造成線程池這是第一步,然后改造數據庫連接對象池,數據集對象池。。。。。。
這樣實現有什么好處?
對象池中的對象只是實時被占用,這個占用只發生在一個客戶的一個事件動作發生的那一瞬間,當這個事件一完成,對象即歸還對象池中(只是歸還池中,並沒有釋放對象)。對象避免了頻繁地創建和釋放,減少了許多對象創建所消耗的時間(對象的創建是比較耗時的,何況是如此眾多的對象的創建,這要消耗多少時間)。減少了因對象頻繁地創建和釋放所產生的內存碎片(對象的每一次創建和釋放都會產生微量的內存碎片,看似微不足道。但你想過沒有:服務器是7*24運行的,且頻繁地創建和釋放的對象數量是如此眾多,這會產生多么大的內存碎片啊。內存碎片一是會影響服務器的執行性能,二個可能使服務器當機)。
如何控制最大的客戶連接數量?
設置服務端線程池的上限(PoolSize)吧,控制了線程池最大創建線程的數量,就控制了最大的客戶連接數量。
這個上限設置多少合適?
具體要看那台服務器的硬件配置情況了。實際進行壓力測試才可得知。