2013-05-08 17 views
5

Jestem zaskoczony. Być może ktoś może rzucić trochę światła na zachowanie klienta WCF, którego obserwuję.Korzystanie z WCF, zamykanie i rozszerzenia

Korzystając z próbek WCF, zacząłem grać z różnymi podejściami do komunikacji klient/serwer WCF. Podczas wykonywania 1M żądania testowego równolegle, używałem SysInternals TcpView do monitorowania otwartych portów. Teraz, istnieją co najmniej 4 różne sposoby, aby zadzwonić do klienta:

  1. utworzyć klienta, rób swoje i niech GC zbierać to
  2. Tworzenie klienta w wykorzystaniem bloku, niż rób swoje
  3. Tworzenie kanału klienta z wykorzystaniem fabrycznie w bloku, nie rób swoje
  4. Tworzenie klienta lub kanału, ale używać WCF Extensions robić swoje rzeczy

teraz, o ile mi wiadomo, tylko opcje 2-4, jawnie zadzwoń client.Close(). Podczas ich wykonywania widzę wiele portów pozostawionych w stanie TIME_WAIT. Spodziewałbym się, że opcja 1 będzie najgorszym scenariuszem, z powodu polegania na GC. Jednak, ku mojemu zaskoczeniu, wydaje się być najczystszym z nich wszystkich, co oznacza, że ​​nie pozostawia żadnych długich portów.

Czego mi brakuje?

UPDATE: kod

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

Brakuje jakiegoś kodu źródłowego ... Czy możemy zobaczyć twoje testy jednostkowe? –

+0

Zobacz http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-wcf-client-using-block-issue - użycie bloku może spowodować problemy z WCF. – TrueWill

+0

Dzięki za link, dość ciekawa lektura, ale nadal nie wyjaśnia, dlaczego GC nie pozostawia TIME_WAIT z tyłu, ale client.Close() robi. – Darek

Odpowiedz

1

mam zorientowaliśmy się źródło, myślę. GC nie wywoła opcji Dispose w ClientBase. Dlatego połączenia nie są pozostawione w stanie TIME_WAIT. Postanowiłem więc według tego samego wzoru i stworzył nową WCF rozszerzenia:

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; 
     } 
    } 
} 

ten sposób na koniec wniosek to po prostu przerwać połączenie zamiast zamykania.

+0

Problem z nowym wzorcem polega na tym, że Abort() nie powiadamia usługi o wyłączeniu klienta. Nie wywołując Close() na otwartym połączeniu w bloku try, pozostawiasz otwarte połączenia na serwerze, dopóki nie przestaną działać. Sugerowane czytanie: http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for--cc-client-using-block-issue – ErnieL

+0

Nie sądzę, że tak jest w przypadku @ErnieL. Zgodnie z dokumentacją Microsoft, Abort() powoduje, że obiekt ClientBase przechodzi natychmiast z bieżącego stanu do stanu zamkniętego. Wydaje się to być potwierdzane przez port wyłączający serwer. Czy czegoś brakuje? – Darek

+0

Dokumentacja lubi mówić, że Abort() jest "natychmiastowa", a Close() jest "pełna wdzięku". Na przykład: http://msdn.microsoft.com/en-us/library/ms195520.aspx. Mówiąc tak: Twój wzorzec * nigdy * nie wywołuje Close() i jest dobrze udokumentowany, że Dispose() wywołuje Close(), a nie Abort(). Więc jeśli twój wzorzec jest właściwy, dlaczego Close() w ogóle jest w interfejsie? – ErnieL

Powiązane problemy