2011-10-20 10 views
18

Quindi ho deciso di aumentare le prestazioni un po 'nella mia applicazione WCF e tentare di memorizzare nella cache Canali e ChannelFactory. Ci sono due domande che ho su tutto ciò che ho bisogno di chiarire prima di iniziare.WCF Channel e ChannelFactory Caching

1) Il ChannelFactory deve essere implementato come un singleton?

2) Non sono sicuro di come memorizzare/riutilizzare i singoli canali. Hai qualche esempio su come farlo puoi condividere?

È probabilmente importante notare che il mio servizio WCF viene distribuito come applicazione autonoma, con un solo endpoint.

EDIT:

Grazie per le risposte. Ho ancora qualche domanda però ...

1) Immagino di essere confuso su dove dovrebbe verificarsi la memorizzazione nella cache. Fornisco un'API client che utilizza questo codice a un altro reparto della nostra azienda. Questo caching si verifica sul client?

2) L'API client verrà utilizzata come parte di un'applicazione Silverlight, questo modifica qualcosa? In particolare, quali meccanismi di caching sono disponibili in uno scenario del genere?

3) Non sono ancora chiaro sul design del metodo GetChannelFactory. Se ho un solo servizio, dovrebbe essere creato e memorizzato nella cache solo un ChannelFactory?

non ho ancora implementato alcuna funzione di caching (perché io sono totalmente confusi su come dovrebbe essere fatto!), Ma ecco quello che ho per il proxy client finora:

namespace MyCompany.MyProject.Proxies 
{ 
    static readonly ChannelFactory<IMyService> channelFactory = 
     new ChannelFactory<IMyService>("IMyService"); 

    public Response DoSomething(Request request) 
    { 
     var channel = channelFactory.CreateChannel(); 

     try 
     { 
      Response response = channel.DoSomethingWithService(request); 
      ((ICommunicationObject)channel).Close(); 
      return response; 
     } 
     catch(Exception exception) 
     { 
      ((ICommenicationObject)channel).Abort(); 
     } 
    } 
} 
+0

Per # 3, sì, è necessario creare una sola fabbrica di canali. Fondamentalmente, avrai una fabbrica di canali per ciascuno dei servizi che hai. Nel mio caso, ne abbiamo circa 6 finora, distribuiti principalmente su 2 livelli. Nel tuo caso, se hai solo intenzione di avere un servizio, utilizzare da un'app, puoi semplicemente fare quello che stai facendo sopra. Il codice sopra riportato è sulla strada giusta. Il caching sarà basato sulle esigenze dell'app. – Tim

+0

@Tim-grazie per tutto il tuo aiuto. Lo apprezzo davvero! Penso che nel mio caso il fatto che il mio servizio fosse così "semplice" mi causava confusione nel guardare questi altri esempi dove c'erano più endpoint. Me = meno confuso ora = Tim ha fatto un ottimo lavoro spiegando! Grazie amico! – Didaxis

+0

Sei il benvenuto. Sono contento di poterti aiutare: felice codifica! – Tim

risposta

20

Utilizzare il ChannelFactory per creare un'istanza della factory, quindi memorizzare nella cache quell'istanza. È quindi possibile creare i canali communicatino come necessario/desiderato dall'istanza memorizzata nella cache.

Avete bisogno di più fabbriche di canali (vale a dire, ci sono più servizi)? Nella mia esperienza, è qui che vedrai il più grande beneficio in termini di prestazioni. Creare un canale è un compito abbastanza economico; è tutto pronto all'inizio che richiede tempo.

Non memorizzo nella cache i singoli canali: li creo, li uso per un'operazione e li chiudo. Se li metti in cache, potrebbero scadere e il canale non funzionerà, quindi dovrai abortire e crearne uno nuovo.

Non sono sicuro del motivo per cui si desidera utilizzare singleton per implementare ChannelFactory, soprattutto se si intende crearlo e memorizzarlo nella cache, e c'è solo un endpoint.

Inserirò qualche codice di esempio più tardi quando avrò un po 'più di tempo.

UPDATE: esempi di codice

Ecco un esempio di come ho implementato questo per un progetto di lavoro. Ho usato ChannelFactory<T>, in quanto l'applicazione che stavo sviluppando è un'app di n-tier con diversi servizi, e ne verranno aggiunti altri. L'obiettivo era quello di avere un modo semplice per creare un client una volta per tutta la vita dell'applicazione, e quindi creare canali di comunicazione secondo necessità. Le basi dell'idea non sono mie (l'ho preso da un articolo sul web), anche se ho modificato l'implementazione per i miei bisogni.

Nella mia applicazione ho una classe helper statica e all'interno di tale classe ho un dizionario e un metodo per creare canali di comunicazione dalla fabbrica di channelf.

Il dizionario è il seguente (oggetto è il valore in quanto conterrà diverse fabbriche di canali, uno per ciascun servizio). Inserisco "Cache" nell'esempio come una specie di segnaposto: sostituisci la sintassi con qualsiasi meccanismo di caching che stai utilizzando.

public static Dictionary<string, object> OpenChannels 
{ 
    get 
    { 
     if (Cache["OpenChannels"] == null) 
     { 
      Cache["OpenChannels"] = new Dictionary<string, object>(); 
     } 

     return (Dictionary<string, object>)Cache["OpenChannels"]; 
    } 
    set 
    { 
     Cache["OpenChannels"] = value; 
    } 
} 

Avanti è un metodo per creare un canale di comunicazione dall'istanza di fabbrica. Il metodo controlla se la fabbrica esiste prima - se non lo fa, la crea, la mette nel dizionario e poi genera il canale. In caso contrario, genera semplicemente un canale dall'istanza memorizzata nella cache della fabbrica.

public static T GetFactoryChannel<T>(string address) 
{ 

    string key = typeof(T.Name); 

    if (!OpenChannels.ContainsKey(key)) 
    { 
     ChannelFactory<T> factory = new ChannelFactory<T>(); 
     factory.Endpoint.Address = new EndpointAddress(new System.Uri(address)); 
     factory.Endpoint.Binding = new BasicHttpBinding(); 
     OpenChannels.Add(key, factory); 
    } 

    T channel = ((ChannelFactory<T>)OpenChannels[key]).CreateChannel(); 

    ((IClientChannel)channel).Open(); 

    return channel; 
} 

Ho tolto questo esempio da quello che uso al lavoro. C'è molto che puoi fare con questo metodo: puoi gestire più associazioni, assegnare credenziali per l'autenticazione, ecc. È praticamente il tuo centro commerciale unico per la generazione di un cliente.

Infine, quando lo uso nell'applicazione, generalmente creo un canale, faccio il mio lavoro e lo chiudo (o lo interrompo se necessario). Ad esempio:

IMyServiceContract client; 

try 
{ 
    client = Helper.GetFactoryChannel<IMyServiceContract>("http://myserviceaddress"); 

    client.DoSomething(); 

    // This is another helper method that will safely close the channel, 
    // handling any exceptions that may occurr trying to close. 
    // Shouldn't be any, but it doesn't hurt. 
    Helper.CloseChannel(client); 
} 
catch (Exception ex) 
{ 
    // Something went wrong; need to abort the channel 
    // I also do logging of some sort here 
    Helper.AbortChannel(client); 
} 

Speriamo che gli esempi sopra ti diano qualcosa da fare. Ho usato qualcosa di simile a questo per circa un anno in un ambiente di produzione e ha funzionato molto bene. Il 99% dei problemi riscontrati di solito sono stati correlati a qualcosa al di fuori dell'applicazione (client esterni o fonti di dati non sotto il nostro controllo diretto).

Fatemi sapere se qualcosa non è chiaro o avete ulteriori domande.

+0

Grazie Tim. Hai davvero fornito alcune informazioni preziose. Sicuramente starò alla ricerca del tuo esempio! – Didaxis

+0

@ user384080 - il codice è nella mia risposta. Se questo non è chiaro, fammi sapere. Grazie. – Tim

+0

@Tim C'è un bug nella tua implementazione. Memorizzi le fabbriche nella cache per tipo di contratto, indipendentemente dall'indirizzo. Dovresti avere una chiave che contenga sia il tipo di contratto che l'indirizzo. – Anubis

5

Si può sempre e solo rendere la vostra ChannelFactory statico per ogni contratto WCF ...

si deve essere consapevoli che dal Net 3.5 gli oggetti proxy vengono raggruppate per motivi di prestazioni da parte del channel factory. La chiamata al metodo ICommunicationObject.Close() restituisce effettivamente l'oggetto al pool nella speranza che possa essere riutilizzato.

Guarderei il profiler se vuoi fare un po 'di ottimizzazione, se puoi evitare una sola chiamata IO nel tuo codice potrebbe superare di gran lunga qualsiasi ottimizzazione tu farai con la fabbrica del canale. Non selezionare un'area da ottimizzare, utilizzare il profiler per individuare dove è possibile targetizzare un'ottimizzazione. Se si dispone di un database SQL, ad esempio, nelle query si troverà probabilmente un po 'di frutta bassa che ti farà aumentare gli ordini di magnitudo se questi non sono già stati ottimizzati.

3

La creazione del canale costa così tanto le prestazioni. in realtà, WCF ha già il meccanismo di cache per ChannelFactory se si utilizza ClientBase nel client anziché in puro ChannelFactory. Ma la cache sarà scaduta se si eseguono alcune operazioni andizionali (si prega di google per i dettagli se si desidera). Per il problema di ErOx ho un'altra soluzione penso che sia meglio. vedi sotto:


namespace ChannelFactoryCacheDemo 
{ 
    public static class ChannelFactoryInitiator 
    { 
     private static Hashtable channelFactories = new Hashtable(); 

     public static ChannelFactory Initiate(string endpointName) 
     { 
      ChannelFactory channelFactory = null; 

      if (channelFactories.ContainsKey(endpointName))//already cached, get from the table 
      { 
       channelFactory = channelFactories[endpointName] as ChannelFactory; 
      } 
      else // not cached, create and cache then 
      { 
       channelFactory = new ChannelFactory(endpointName); 
       lock (channelFactories.SyncRoot) 
       { 
        channelFactories[endpointName] = channelFactory; 
       } 
      } 
      return channelFactory; 
     } 
    } 
    class AppWhereUseTheChannel 
    { 
     static void Main(string[] args) 
     { 
      ChannelFactory channelFactory = ChannelFactoryInitiator.Initiate("MyEndpoint"); 
     } 
    } 

    interface IMyContract { } 
} 

è possibile personalizzare la logica ed i parametri del metodo Iniziato da soli se si ha un altro requisito. ma questa classe di iniziatori non è limitata a un solo endpoint. è potente per tutti gli endpoint nella tua applicazione. fiduciosamente. funziona bene per te. BTW. questa soluzione non è da parte mia l'ho preso da un libro

+0

Si noti che il 'lock' è usato in modo errato. Il blocco dovrebbe essere preso anche sulla chiamata a 'ContainsKey'. –

Problemi correlati