2013-05-08 11 views
5

Sono perplesso. Forse qualcuno può far luce sul comportamento del client WCF che sto osservando.WCF utilizzo, chiusura ed estensioni

Utilizzando gli esempi WCF, ho iniziato a giocare con approcci diversi alla comunicazione client/server WCF. Mentre eseguivo 1M di richieste di test in parallelo, stavo usando SysInternals TcpView per monitorare le porte aperte. Ora, ci sono almeno 4 modi diversi di chiamare il cliente:

  1. Creare il client, fare la vostra cosa, e lasciare GC ritirarlo
  2. Creare il cliente in un blocco utilizzando, di fare la tua cosa
  3. Creare il canale del client dalla fabbrica in un blocco utilizzando, di quanto non facciano la tua cosa
  4. Creare il client o il canale, ma l'uso WCF Extensions per fare la tua cosa

Ora, a mia conoscenza, solo le opzioni 2-4, chiamare esplicitamente client.Close(). Durante la loro esecuzione vedo molte porte lasciate nello stato TIME_WAIT. Mi aspetto che l'opzione 1 sia la peggiore delle ipotesi, a causa della dipendenza dal GC. Tuttavia, con mia sorpresa, sembra che sia il più pulito di tutti, nel senso che non lascia dietro di sé alcuno sbocco.

Cosa mi manca?

UPDATE: Codice sorgente

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

Ti manca qualche codice sorgente ... Potremmo vedere i test dell'unità? –

+0

Vedere http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue - il blocco using può causare problemi con WCF. – TrueWill

+0

Grazie per il collegamento, una lettura abbastanza interessante, ma non spiega ancora perché GC non lascia TIME_WAITs indietro, ma client.Close(). – Darek

risposta

1

ho capito, credo. Il GC non chiamerà Dispose su ClientBase. Ecco perché le connessioni non vengono lasciate in uno stato TIME_WAIT. Così ho deciso di seguire lo stesso modello e ha creato una nuova estensione WCF:

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

In questo modo, al termine di una richiesta sarà semplicemente interrompere la connessione, invece di chiuderlo.

+0

Il problema con il nuovo modello è che Abort() non notifica il servizio di arresto del client. Non chiamando Close() su una connessione aperta nel blocco try, le connessioni rimangono aperte sul server fino al timeout. Lettura suggerita: http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue – ErnieL

+0

Non credo che sia il caso di @ErnieL. Per documentazione Microsoft, Abort() causa l'oggetto ClientBase per passare immediatamente dallo stato corrente allo stato chiuso. Questo sembra essere confermato dalla porta che spegne il lato server. Mi sto perdendo qualcosa? – Darek

+0

Alla documentazione piace dire che Abort() è "immediato" e Close() è "grazioso". Ad esempio: http://msdn.microsoft.com/en-us/library/ms195520.aspx. Metti in questo modo: il tuo pattern * mai * chiama Close() e il suo ben documentato che Dispose() chiama Close() e non Abort(). Quindi, se il tuo modello è corretto, perché è Close() nell'interfaccia? – ErnieL

Problemi correlati