2012-03-19 30 views
2

我有一個使用WCF連接到其他服務的Windows服務。它檢查它們是否還活着,獲取這些服務所具有的任何錯誤消息,並報告這些錯誤消息。每隔30秒使用通道工廠檢查一次,其中爲符合接口的配置中找到的每個服務創建代理。運行好幾天後,服務器變得無響應,並開始報告「RPC服務器不可用錯誤」。我可以使用計算機管理連接到它,它的內存足跡似乎沒有攀升,但如果我停止服務,它完全解決了這個問題。我已附加了我正在使用的渠道工廠經理,但如果需要其他任何內容,請讓我知道。難道服務渠道沒有被正確釋放?我能做些什麼來診斷呢?有沒有人遇到過這個?診斷由Windows服務調用WCF造成的「RPC服務器不可用錯誤」WCF

public class ChannelFactoryManager : IDisposable 
{ 
    private static Dictionary<Tuple<Type, string>, ChannelFactory> _factories = new Dictionary<Tuple<Type, string>, ChannelFactory>(); 
    private static readonly object _syncRoot = new object(); 

    public virtual T CreateChannel<T>() where T : class 
    { 
     return CreateChannel<T>("*", null); 
    } 

    public virtual T CreateChannel<T>(string endpointConfigurationName) where T : class 
    { 
     return CreateChannel<T>(endpointConfigurationName, null); 
    } 

    public virtual T CreateChannel<T>(string endpointConfigurationName, string endpointAddress) where T : class 
    { 
     T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel(); 
     ((IClientChannel)local).Faulted += ChannelFaulted; 
     return local; 
    } 

    protected virtual ChannelFactory<T> GetFactory<T>(string endpointConfigurationName, string endpointAddress) where T : class 
    { 
     lock (_syncRoot) 
     { 
      ChannelFactory factory; 
      if (!_factories.TryGetValue(new Tuple<Type, string>(typeof(T), endpointConfigurationName), out factory)) 
      { 
       factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress); 
       _factories.Add(new Tuple<Type, string>(typeof(T), endpointConfigurationName), factory); 
      } 
      return (factory as ChannelFactory<T>); 
     } 
    } 

    private ChannelFactory CreateFactoryInstance<T>(string endpointConfigurationName, string endpointAddress) 
    { 
     ChannelFactory factory = null; 
     if (!string.IsNullOrEmpty(endpointAddress)) 
     { 
      factory = new ChannelFactory<T>(endpointConfigurationName, new EndpointAddress(endpointAddress)); 
     } 
     else 
     { 
      factory = new ChannelFactory<T>(endpointConfigurationName); 
     } 
     factory.Faulted += FactoryFaulted; 
     factory.Open(); 
     return factory; 
    } 

    private void ChannelFaulted(object sender, EventArgs e) 
    { 
     IClientChannel channel = (IClientChannel)sender; 
     channel.Abort(); 
    } 

    private void FactoryFaulted(object sender, EventArgs args) 
    { 
     ChannelFactory factory = (ChannelFactory)sender; 
     factory.Abort();    

     Type[] genericArguments = factory.GetType().GetGenericArguments(); 
     if ((genericArguments != null) && (genericArguments.Length == 1)) 
     { 
      Type type = genericArguments[0]; 
      string endPointName = factory.Endpoint.Name; 
      Tuple<Type, string> key = new Tuple<Type, string>(type, endPointName); 

      if (_factories.ContainsKey(key)) 
      { 
       _factories.Remove(key); 
      } 
     } 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      lock (_syncRoot) 
      { 
       foreach (Tuple<Type, string> type in _factories.Keys) 
       { 
        ChannelFactory factory = _factories[type]; 
        try 
        { 
         factory.Close(); 
         continue; 
        } 
        catch 
        { 
         factory.Abort(); 
         continue; 
        } 
       } 
       _factories.Clear(); 
      } 
     } 
    } 
} 

感謝 羅布

+0

您所描述的問題當然聽起來像您的配置代碼沒有按預期工作。如果你想削減這個[Gordian Knot](http://en.wikipedia.org/wiki/Gordian_knot),去掉靜態集合並且在一個好的WCF客戶端中嘗試每個服務代理try/catch模式創建>嘗試>調用>關閉> catch>中止)您的選擇。性能將受到影響,但至少你可以指望不泄漏網絡資源,就好像你現在正在做的那樣:) – 2012-03-19 15:15:19

+0

好的,謝謝我會放棄這一切。性能不應該是那麼大的問題,所以也許這樣做會是一個更好的選擇。 – bobwah 2012-03-19 15:59:11

回答

1

如果用實例化服務代理需要走的路線,在這SO question的答案給出的代理實例處置一些選擇和理由。作爲一個基本的開始,我建議:

//Your client type could be ICommunicationObject or ClientBase: 
var client = new YourServiceProxyType(); 
try { 
    var result = client.MakeCall(); 
    //do stuff with result... 

    //Done with client. Close it: 
    client.Close(); 
} 
catch (Exception ex) { 
    if (client.State != System.ServiceModel.CommunicationState.Closed) 
     client.Abort(); 
} 

在設計一個良好的WCF代理處置模式的根本問題是WCF團隊在微軟決定在可以拋出異常從而防止非託管發行的方式實現Dispose直到調用Abort()或者代理實例完全被垃圾收集。他們編寫了框架,以便他們做出選擇,不幸的是我們不得不承受後果。

+0

謝謝我暫時這樣做,並將其標記爲正確答案。我想在某種程度上抽象出IoC和單元測試的東西,至少我知道問題在於處理並可以解決這個問題。 – bobwah 2012-03-22 09:30:41