前言
最近系統上線完修改完各種bug之后,功能上還算是比較穩定,由於最近用戶數的增加,不知為何經常出現無法登錄、頁面出現錯誤等異常,后來發現是由於WCF服務時不時的就死掉了。后來就開始分析問題。得到的初步解決方案如下:
1、在Web端調用WCF服務使用后,未釋放未關閉導致新的鏈接無法訪問
2、增加默認的連接數,系統默認的鏈接數比較小
3、提供同一個WCF服務的不同實例
1、在Web端調用WCF服務使用后,未釋放未關閉導致新的鏈接無法訪問
首先保證客戶端每次建立的連接在使用完成后進行關閉。那么請不要使用傳統的using語句中來調用WCF,這里@dudu大神也曾經有遇到過這個問題 http://www.cnblogs.com/dudu/archive/2011/01/18/1938144.html。對其分析也比較全面,在此不再贅述。
不過自己感覺更好的處理方式可能是下面這樣,也就是將@dudu中的方法進行了簡單的封裝,但自己感覺還有優化的空間,暫時還沒試出來。
public static class WcfExtensions { public static void Using<T>(this T client, Action<T> work) where T : ICommunicationObject { try { work(client); client.Close(); } catch (CommunicationException e) { client.Abort(); } catch (TimeoutException e) { client.Abort(); } catch (Exception e) { client.Abort(); } } }
進行調用看起來是如下的方式,看上去還是比較簡練了,但是感覺還是有些繁瑣,不知道能不能直接一行return代碼搞定?
public static DocAppend GetDocAppend(string dwid, string actionId) { DocAppend docAppend = new DocAppend(); new DocumentServiceV2.DocumentServiceV2Client().Using(channel => docAppend = channel.GetDocAppend(dwid, actionId)); return docAppend; }
另外一種關閉鏈接的方式,這種方式其實和上面那種大同小異,也是可以封裝的,系統中暫且就使用的上面的方式。
Document document = null; DocumentServiceClient client = new DocumentService.DocumentServiceClient(); try { document= client.GetDocument(id); if (client.State != System.ServiceModel.CommunicationState.Faulted) { client.Close(); } } catch (Exception ex) { client.Abort(); } return document;
2、增加默認的連接數,系統默認的鏈接數比較小
如果采用的netTcp綁定,而在windows7中,並發連接數默認是10。
這是原來的配置文件
<binding name="netTcpBindConfig" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647">
將項目移植到服務器上之后
<binding name="netTcpBindConfig" closeTimeout="00:30:00" openTimeout="00:30:00" receiveTimeout="00:30:00" sendTimeout="00:30:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="100" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="100" maxReceivedMessageSize="2147483647">
但有些時候還是不能解決問題,就想到是不是需要配置一下行為,於是將行為的連接數量也改變了
<serviceBehaviors> <behavior name="ThrottledBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> <dataContractSerializer maxItemsInObjectGraph="2147483647"/> <serviceThrottling maxConcurrentCalls="5000" maxConcurrentSessions="5000" maxConcurrentInstances="5000" /> </behavior> </serviceBehaviors>
maxConcurrentCalls:在同一時刻允許處理的最大服務器操作數。如果超過次數,則需要把其他方法調用插入隊列中,以等待處理。
maxConcurrentSessions:同時傳輸或應用程序會話的最大個數。
maxConcurrentInstances:實例的最大個數。
在Http協議中,規定了同個Http請求的並發連接數最大為2. 這個數值,可謂是太小了。而目前的瀏覽器,已基本不再遵循這個限制,但是Dot Net平台上的 System.Net 還是默認遵循了這個標准的。從而造成了,在使用HttpWebRequset 或者 WebClient 利用多線程的方式,訪問某個網站時,經常出現 連接被異常關閉 的錯誤,大大降低了效率。
這個限制的值,是可以自己設置或配置的。此值設置后,只對以后發起的HTTP請求有效。
<system.net> <connectionManagement> <add address="*" maxconnection="5000"/> </connectionManagement> </system.net>
3、提供同一個WCF服務的不同實例
3、首先查看一個WCF服務類
里面有N多構造函數的重載版本,我們來具體看一下第二個構造函數
public DocumentWriteServiceClient(string endpointConfigurationName) : base(endpointConfigurationName) { }
即傳入配置名生與代碼類的實例,我們在web.config中的wcf配置節,做如下處理:
<endpoint address="http://localhost:8700/Design_Time_Addresses/SinoSZJS.WebWCF/DocumentWriteService/" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICommonBinding" contract="DocumentWriteService.IDocumentWriteService" name="1"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="http://localhost:8700/Design_Time_Addresses/SinoSZJS.WebWCF/DocumentWriteService/" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICommonBinding" contract="DocumentWriteService.IDocumentWriteService" name="2"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="http://localhost:8700/Design_Time_Addresses/SinoSZJS.WebWCF/DocumentWriteService/" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICommonBinding" contract="DocumentWriteService.IDocumentWriteService" name="3"> <identity> <dns value="localhost" /> </identity> </endpoint>
修改客戶端的調用代碼
DocumentWriteServiceClient client = new DocumentWriteServiceClient();
改為
DocumentWriteServiceClient client = new DocumentWriteServiceClient(new Random().Next(1, 4).ToString());
即客戶端隨機從多個wcf服務端的host中挑一個,生成代碼類實例,說白了就是把一個wcf的host分身成了3個,並且客戶端隨機調用3者之一。
如果要考慮到大量並發的情況下,偽隨機數可能確實有一些問題,不過,這個應該也不難解決,自己另外寫一個類似偽隨機數的算法,只要保證生成指定范圍內不重復的數字(或字符)就可以了。
總結
暫時這三種方式有效地防止了WCF服務的再次掛掉,至少最近幾天服務一直在穩定的運行,沒有太大的異常,很是讓人欣慰。不知道針對WCF服務的處理是否還有其他方式,也讓博客園的大牛們來指點一二吧。