2013-04-05 41 views
3

WCF可靠的會話故障時重CPU負荷或在服務器線程池忙

有似乎是在WCF可靠的會話一個設計缺陷,防止問題或者當服務器處於CPU負載過高(80-100%範圍)或者沒有可用於處理消息的直接IO線程池線程時接受基礎架構保持活動消息。由於可靠的會話不活動超時,症狀表現爲明顯的隨機通道中止。然而,中止邏輯似乎以更高的優先級或通過不同的機制運行,因爲即使保持活動定時器不能運行,中止計時器似乎也會啓動。WCF可靠的會話故障時重CPU負荷或在服務器線程池忙

挖掘到參考源,看來該ChannelReliableSession使用一個InterruptableTimer類來處理靜止定時器。作爲響應,它觸發由ReliableOutputSessionChannel設置的PollingCallback,它創建一個ACKRequestedMessage並將其發送到遠程端點。 InactivityTimer使用WCF內部的IOThreadTimer/IOThreadScheduler來調度自己。這取決於可用(非繁忙)的IO ThreadPool線程來爲定時器提供服務。 如果CPU負載很高,看起來線程池不會產生新的線程。因此,如果有多個線程正在執行(在我的4核機器上顯示爲8個線程; 15秒鐘不活動TimeTimeout 7將中止並失敗),則沒有線程可用於發送保持活動狀態。但是,如果將客戶端上的可靠會話不活動超時修改爲比服務器更長,即使在這些情況下,服務器仍會單方面中止通道,因爲它期望在較短時間內發送消息。所以看起來中止邏輯運行在更高的優先級或者拋出一個異常進入一個正在執行的線程(不確定哪個);我預計服務器上的中止會由於高CPU而延遲,並且客戶端的更長暫停時間最終會被觸發,但事實並非如此。 如果CPU負載較低,那麼即使併發呼叫需要30-90秒才能返回,完全相同的情況也可以很好地工作。

您的InstanceMode是什麼,最大併發連接數,會話數或實例數是什麼,其他超時值是什麼(除了recieveTimeout必須大於inactivityTimeout)是無關緊要的。這完全是WCF實施的一個設計缺陷。它應該使用隔離的高優先級或實時線程來處理保持活動的消息,以避免虛假的異常終止。

簡短版本是:我可以發出1000個併發請求,需要60秒才能完成15秒可靠會話不活動超時沒有問題,只要CPU負載保持低。一旦CPU負載得到重呼,呼叫將隨機開始中止,包括未佔用任何CPU時間的呼叫或閒置等待使用的雙工會話如果傳入的呼叫也增加CPU負載,則服務將進入死亡螺旋狀,因爲執行時間浪費在保證中止的請求上,而其他請求位於入站隊列中。直到所有請求都停止,所有正在運行的線程完成並且CPU負載下降,服務才能恢復到健康狀態。 這種行爲看似矛盾地使可靠會話成爲最不可靠的通信機制之一。

此相同的行爲適用於客戶;在這種情況下,WCF客戶端可能受到盒子上其他進程的控制,但是在CPU負載過高的情況下,它會隨機放棄可靠會話,除非所有操作的時間少於不活動時間的完成時間,但如果您不發出新呼叫快速WCF可能仍然無法發送保持活動狀態,並且通道可能會出錯。

回答

3

文檔化我的回答:

如果使用ThreadPool.SetMinThreads(X,Y),其中Y不是執行的併發請求的WCF線程的數量更大一些數字可以稍微減輕這個問題。然後,可能有線程可用++來服務保持活動狀態,即使在持續100%的CPU負載下,可靠會話也可能不會超時,但這也有其限制。在我的測試中,我將IO線程的數量從2增加到了20,然後發佈了大量的併發請求(但不做任何簡單的睡眠10秒)。之後,我重新運行我的客戶端,但是CPU浪費了電話,我能夠同時成功執行所有8個操作。重新啓動服務,然後立即執行相同的客戶端測試失敗,這是由於線程池的延遲初始化。如果將此舉反過來,我最終開始在14個同時調用(10個調用中止)時再次觸發超時,這可能只是調度程序沒有獲得足夠的CPU片段來正確執行。我懷疑如果你能抓住IO線程並增加它們的優先級,你可能會解決這個問題。

++因爲池使用延遲初始化,所以您必須從需要時間完成但不使用任何CPU的客戶端發出足夠的併發調用(例如:Thread.Sleep(5000))來強制池來創建最小線程數而不觸發高CPU塊 - 新線程邏輯,否則將不會創建最小線程數,並且問題仍然會發生。

另一個可能的解決方法是使inactivityTimeout成爲一個非常大的值。這將有助於緩解問題,但會引入新的拒絕服務漏洞,甚至會導致客戶端無意中關閉連接。

否則目前似乎沒有解決此問題的方法;我個人建議不要使用可靠會話,因爲它會使連接中止和中止開始發生的情況中的中止變得隨機。