2011-06-29 153 views
8

我有一個實時應用程序,可以跟蹤全國各地許多網站的資產。作爲該解決方案的一部分,我有8個客戶端應用程序更新中央服務器。處理WCF超時的最佳方式

我的問題是,有時應用程序會失去與中央服務器的連接,我想知道處理這個問題的最佳方法是什麼?我知道我可以增加最大發送/接收時間來處理超時,但我也想要一個優雅的解決方案來處理,如果連接到服務器關閉:

例如我打電話給我這樣的服務:

using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient()) 
{ 
    statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString()); 
} 

我想添加一個try/catch這樣的......

using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient()) 
{ 
    try 
    { 
     statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString()); 
    } 
    catch (TimeoutException timeout) 
    { 
     LogMessage(timeout); 
    } 
    catch (CommunicationException comm) 
    { 
     LogMessage(comm); 
    } 
} 

處理這種方式不允許我重新運行代碼,而無需一噸碼重複。任何人有任何建議?

編輯:縱觀Sixto Saez和user24601有一個整體解決方案的答案比在單個異常級別上處理超時要好,但是......我在考慮下面的問題會解決我的問題(但它會添加一個TON的額外代碼錯誤處理):

void Method(int statusId) 
{ 
    var statusRepository = new StatusRepositoryClient.StatusRepositoryClient() 

     try 
     { 
     IsServerUp(); 
     statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString()); 
     statusRepository.Close(); 
     }    
     catch (Exception ex) 
     { 
      statusRepository.Abort(); 

      if (ex is TimeoutException || ex is CommunicationException) 
      { 
       LogMessage(timeout); 
       Method(statusId); 
      } 
      else 
      { 
       throw new Exception(ex.Message + ex.InnerException); 
      } 
     } 

    } 
} 

bool IsServerUp() 
{ 
    var x = new Ping(); 
    var reply = x.Send(IPAddress.Parse("127.0.0.1")); 

    if (reply == null) 
    { 
     IsServerUp(); 
    } 
    else 
    { 
     if (reply.Status != IPStatus.Success) 
     { 
      IsServerUp(); 
     } 
    } 

    return true; 
} 
+0

您可以編寫一個函數來測試服務器是否啓動,這樣您可以在連接到資源庫之前檢查服務器是否已啓動。你也可以看看直到服務器啓動。 – Jethro

+0

因此,做一個遞歸函數,不退出,直到服務器,然後繼續......並在任何wcf調用之前彈出呼叫?我喜歡這個想法,並且它比上面提到的編輯代碼要少。您的想法絕對可以提高99%的可靠性,但是如果服務器在檢查和方法調用之間失去連接,我該如何處理呢? –

+0

@Jethro:最佳做法建議不要使用ping方法。看到這裏的討論:http://stackoverflow.com/questions/2166356/how-to-check-if-a-wcf-service-is-operational基本的想法是,超時可能是一個服務依賴的結果,它wouldn不會顯示在ping事件中(例如與數據庫導致超時的數據庫交互的WCF服務) – VoteCoffee

回答

3

對於初學者,我認爲你的Wcf錯誤處理是錯誤的。它應該看起來像這樣

var statusRepository = new StatusRepositoryClient.StatusRepositoryClient(); 
try 
{ 
    statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString()); 
    statusRepository.Close() 
} 
catch(Exception e) 
{ 
    statusRepository.Abort(); 
    LogMessage(e); 
    throw; //I would do this to let user know. 
} 

我也會重新拋出錯誤讓用戶知道這個問題。

+0

我處理關閉/中止以使垃圾收集器在using語句完成時自動觸發有什麼好處? –

+1

這裏是[一個很好的帖子](http://geekswithblogs.net/DavidBarrett/archive/2007/11/22/117058.aspx)閱讀關於WCF中的Dispose模式實現,以及爲什麼你不應該使用using語句它, –

1

在設計您的異常處理之前,需要做出的一個重要決定是您希望確保客戶端發送的每條消息的傳遞還是可以讓服務「丟失」一些。對於guaranteed delivery,,假設客戶端可以配置爲支持netMsmqBinding,那麼最好的內置解決方案就是netMsmqBinding。否則,WCF中內置了一個輕量級的reliable messaging capability。如果您嘗試通過異常處理純屬處理郵件傳送你會下降一個兔子洞... :)

+0

謝謝你會看看 –

2

我有一個雙管齊下的方法來驗證服務器已經啓動:

1)I每5秒建立一個「PING」到服務器。服務器響應一個'PONG'和一個額定負載(低,中,高,因此客戶端可以調整它在服務器上的負載)。如果客戶端EVER沒有收到乒乓球,它會認爲服務器已關閉(因爲這對服務器的壓力非常低 - 只需聆聽並響應)。

2)您正在捕獲的隨機超時會記錄在ConnectionMonitor類中以及所有成功的連接。這些調用中的一個調用超時是不足以將服務器關閉的,因爲有些調用可能非常重,或者可能需要很長時間。但是,這些比例足夠高會導致應用程序進入服務器超時。

我也不想爲每個連接超時發出一條消息,因爲使用較差服務器的人(或者只是一些位於實驗室中的計算機作爲服務器)發生的頻率過高。我的大多數電話可能會錯過一次或兩次,但錯過5或6次電話顯然會導致入侵。

當服務器超時狀態發生時,我拋出一個小小的對話框來解釋用戶發生了什麼。

1

嗨請參閱下面的解決方案。另外請注意下面的代碼沒有被編譯,所以可能有一些邏輯和輸入錯誤。

bool IsServerUp() 
{ 
    var x = new Ping(); 
    var reply = x.Send(IPAddress.Parse("127.0.0.1")); 

if (reply == null) return false; 

return reply.Status == IPStatus.Success ? true : false; 
} 

int? GetStatusId() 
{ 
try 
{ 
    using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient()) 
    { 
     return statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString()); 
    } 
}catch(TimeoutException te) 
{ 
    //Log TimeOutException occured 
    return null; 
} 
} 

void GetStatus() 
{ 
try 
{ 
    TimeSpan sleepTime = new TimeSpan(0,0,5); 
    int maxRetries = 10; 

    while(!IsServerUp()) 
    { 
     System.Threading.Thead.Sleep(sleepTime); 
    } 

    int? statusId = null; 
    int retryCount = 0; 

    while (!statusId.HasValue) 
    { 
     statusId = GetStatusId(); 
     retryCount++; 

     if (retryCount > maxRetries) 
      throw new ApplicationException(String.Format("{0} Maximum Retries reached in order to get StatusId", maxRetries)); 
     System.Threading.Thead.Sleep(sleepTime); 
    } 
}catch(Exception ex) 
{ 
    //Log Exception Occured 
} 
}