Diverse forms dell’interfaccia del contratto di servizio WCF

Sembra che io possa passare liberamente tra le seguenti tre versioni diverse della stessa API dell’interfaccia di contratto WCF, senza rompere i client:

[ServiceContract] interface IService { // Either synchronous // [OperationContract] // int SomeMethod(int arg); // Or TAP [OperationContract] Task SomeMethodAsync(int arg); // Or APM // [OperationContract(AsyncPattern = true)] // IAsyncResult BeginSomeMethod(int arg, AsyncCallback callback, object state); // int EndSomeMethod(IAsyncResult ar); } 

L’app client di test esistente continua a funzionare senza alcuna ricompilazione o touch. Se faccio ricompilare il servizio e reimportare il suo riferimento nell’app client, la definizione WSDL rimane la stessa , 1: 1.

Le mie domande:

  • Posso fare affidamento su un comportamento legittimo? È documentato da qualche parte?

L’idea è di convertire un insieme di metodi sincroni SomeMethod stile SomeMethod in metodi TAP SomeMethodAsync , per usare async/await nella loro implementazione e migliorare così la scalabilità del servizio WCF, senza rompere i client esistenti.

Inoltre, ci sono stati problemi noti con il ridimensionamento del servizio WCF in .NET 3.5 e .NET 4.0. Sono documentati nell’articolo MSKB “Il servizio WCF può aumentare gradualmente sotto carico” e l’articolo CodeProject “Tweaking WCF per creare un’API REST asincrona altamente scalabile” . Fondamentalmente, non era sufficiente implementare le API del contratto di servizio come naturalmente asincrone, il runtime di WCF stava ancora bloccando il thread di richiesta.

  • Qualcuno sa se questo problema è stato risolto per .NET 4.5.x , out-of-the-box? O le modifiche aggiuntive sono ancora necessarie?

Le operazioni WCF possono essere definite utilizzando TAP sincrono, EAP o (a partire da .NET 4.5). Da MSDN :

I clienti possono offrire allo sviluppatore qualsiasi modello di programmazione che scelgono, purché sia ​​rispettato il modello di scambio dei messaggi sottostante. Allo stesso modo, i servizi possono implementare le operazioni in qualsiasi modo, purché sia ​​rispettato il modello di messaggio specificato.

Puoi effettivamente avere tutti e 3 i modelli in un’unica interfaccia di contratto e tutti si riferiscono allo stesso messaggio.

Sul filo, non c’è differenza nel modo in cui esegui le operazioni. WSDL (che WCF costruisce da ogni endpoint ABC – indirizzo, associazione e contratto) non contiene queste informazioni. Viene generato dalle descrizioni delle operazioni .

Se si osserva la class OperationDescription , che è utilizzata in una ContractDescription , vedrai che ciascuna operazione ha queste proprietà: SyncMethod , BeginMethod , EndMethod e TaskMethod . Quando si crea una descrizione, WCF combina tutti i metodi in base al nome dell’operazione in un’unica operazione. Se c’è qualche discrepanza tra le operazioni con lo stesso nome in modelli diversi (ad es. Parametri diversi), WCF genererebbe un’eccezione che specifica esattamente cosa non va. WCF assume automaticamente (opzionale) il suffisso “Async” per i metodi basati su attività e il prefisso Begin / End per APM.

Il lato client e server sono completamente indipendenti in questo senso. L’utilità che genera classi proxy da WSDL ( svcutil ), può generare proxy per qualsiasi modello di esecuzione. Non deve nemmeno essere un servizio WCF.

Sul lato server, se viene implementato più di un pattern, WCF ne utilizzerà uno solo nel seguente ordine di precedenza: Task, Sync e APM. Questo è documentato da qualche parte in MSDN, proprio non riesco a trovarlo adesso. Ma puoi guardare la fonte di riferimento qui .

In conclusione, è ansible modificare in modo sicuro l’implementazione del server se non si modifica il messaggio rappresentato dall’operazione.

Per quanto riguarda il ridimensionamento (dovrebbe essere una domanda diversa IMO)

  • I valori predefiniti di limitazione della WCF sono stati aggiornati in .NET 4.5 a valori molto più ragionevoli e sono ora dipendenti dal processore (vedere qui ).
  • Non c’è alcun cambiamento riguardo al problema del pool di thread. Il problema deriva dalla dimensione iniziale del pool di thread della porta di completamento, che è inizialmente impostato su 4 volte la quantità dei processori logici. È ansible utilizzare ThreadPool.SetMinThreads per aumentare l’importo di qualche fattore (vedere questo post ). Questa impostazione potrebbe essere utile anche dal lato client.

Se si utilizza async sul lato server (quando si chiama altri servizi, database, ecc.), La situazione di threading potrebbe migliorare notevolmente perché non si sprecheranno thread thread-pool che stanno solo aspettando il completamento dell’IO.

La cosa migliore in queste situazioni è fare MOLTO benchmarking.