2013-05-08 11 views
5

Ich bin ratlos. Vielleicht kann jemand etwas Licht auf das WCF-Kundenverhalten werfen, das ich beobachte.WCF mit, Schließen und Erweiterungen

Mit den WCF-Samples habe ich angefangen, mit verschiedenen Ansätzen zur WCF-Client/Server-Kommunikation zu spielen. Bei der parallelen Ausführung von 1M Testanforderungen verwendete ich SysInternals TcpView, um offene Ports zu überwachen. Nun gibt es mindestens 4 verschiedene Möglichkeiten, um den Client zu nennen:

  1. den Client erstellen, machen Sie Ihre Sache, und lassen GC sammeln es
  2. Erstellen Sie den Client in einem using-Block, als mach dein Ding
  3. erstellen
  4. den Client-Kanal von der Fabrik in einem mit Block, als mach dein Ding
  5. erstellen Sie den Client oder den Kanal, sondern verwenden WCF Extensions Ihre Sache

jetzt zu tun ist, meines Wissens, nur Optionen 2-4, explizit anrufen client.Close(). Während ihrer Ausführung sehe ich viele Ports im Zustand TIME_WAIT. Ich würde erwarten, dass Option 1 das Worst-Case-Szenario ist, weil man sich auf den GC verlässt. Zu meiner Überraschung scheint es jedoch das sauberste von allen zu sein, das heißt, es hinterlässt keine bleibenden Ports.

Was fehlt mir?

UPDATE: Quellcode

private static void RunClientWorse(ConcurrentBag<double> cb) 
    { 
     var client = new CalculatorClient(); 
     client.Endpoint.Address = new EndpointAddress("net.tcp://localhost:8000/ServiceModelSamples/service"); 
     RunClientCommon(cb, client);       
    } 

    private static void RunClientBetter(ConcurrentBag<double> cb) 
    { 
     using (var client = new CalculatorClient()) 
     { 
      client.Endpoint.Address = new EndpointAddress("net.tcp://localhost:8000/ServiceModelSamples/service"); 
      RunClientCommon(cb, client); 
     } 
    } 

    private static void RunClientBest(ConcurrentBag<double> cb) 
    { 
     const string Uri = "net.tcp://localhost:8000/ServiceModelSamples/service"; 
     var address = new EndpointAddress(Uri); 
     //var binding = new NetTcpBinding("netTcpBinding_ICalculator"); 
     using (var factory = new ChannelFactory<ICalculator>("netTcpBinding_ICalculator",address)) 
     { 
      ICalculator client = factory.CreateChannel(); 
      ((IContextChannel)client).OperationTimeout = TimeSpan.FromSeconds(60); 
      RunClientCommon(cb, client); 
     } 
    } 

    private static void RunClientBestExt(ConcurrentBag<double> cb) 
    { 
     const string Uri = "net.tcp://localhost:8000/ServiceModelSamples/service"; 
     var address = new EndpointAddress(Uri); 
     //var binding = new NetTcpBinding("netTcpBinding_ICalculator"); 
     new ChannelFactory<ICalculator>("netTcpBinding_ICalculator", address).Using(
      factory => 
       { 
        ICalculator client = factory.CreateChannel(); 
        ((IContextChannel)client).OperationTimeout = TimeSpan.FromSeconds(60); 
        RunClientCommon(cb, client); 
       }); 
    } 
+3

Ihnen fehlt ein Quellcode ... Könnten wir Ihre Komponententests sehen? –

+0

Siehe http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue - der using-Block kann Probleme mit WCF verursachen. – TrueWill

+0

Danke für den Link, recht interessant zu lesen, aber es erklärt immer noch nicht, warum GC keine TIME_WAITs zurücklässt, sondern client.Close(). – Darek

Antwort

1

ich es herausgefunden haben, glaube ich. Der GC wird Dispose in ClientBase nicht aufrufen. Aus diesem Grund bleiben die Verbindungen nicht im Zustand TIME_WAIT. Also beschloss ich, das gleiche Muster zu folgen und eine neue WCF-Erweiterung erstellt:

public static void UsingAbort<T>(this T client, Action<T> work) 
     where T : ICommunicationObject 
    { 
     try 
     { 
      work(client); 
      client.Abort(); 
     } 
     catch (CommunicationException e) 
     { 
      Logger.Warn(e); 
      client.Abort(); 
     } 
     catch (TimeoutException e) 
     { 
      Logger.Warn(e); 
      client.Abort(); 
     } 
     catch (Exception e) 
     { 
      Logger.Warn(e); 
      client.Abort(); 
      throw; 
     } 
    } 
} 

diese Weise am Ende einer Anfrage es wird einfach die Verbindung abbrechen, anstatt sie zu schließen.

+0

Das Problem mit Ihrem neuen Muster ist, dass Abort() den Dienst des Client-Shutdowns nicht benachrichtigt. Wenn Sie Close() für eine offene Verbindung im try-Block nicht aufrufen, lassen Sie Verbindungen auf dem Server offen, bis sie ablaufen. Vorgeschlagener Messwert: http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue – ErnieL

+0

Ich glaube nicht, dass der Fall @ErnieL ist. Nach den Microsoft-Dokumentationen bewirkt Abort(), dass das Objekt ClientBase sofort von seinem aktuellen Zustand in den geschlossenen Zustand übergeht. Dies scheint durch die Port-Herunterfahren-Serverseite bestätigt zu sein. Fehle ich etwas? – Darek

+0

Die Dokumentation sagt gerne, dass Abort() "sofort" und Close() "anmutig" ist. Zum Beispiel: http://msdn.microsoft.com/en-us/library/ms195520.aspx. Sagen Sie es so: Ihr Muster * nie * ruft Close() auf und es ist gut dokumentiert, dass Dispose() Close() und nicht Abort() aufruft. Also, wenn Ihr Muster stimmt, warum ist Close() in der Schnittstelle überhaupt? – ErnieL