2016-01-06 14 views
10

Ho 30 aziende secondarie e ognuna ha implementato il proprio servizio web (con diverse tecnologie).Il modo migliore per chiamare molti servizi web?

Ho bisogno di implementare un servizio web per aggregarli, ad esempio tutti i servizi web delle sub-società hanno un metodo web con nome GetUserPoint(int nationalCode) e ho bisogno di implementare il mio servizio web che chiamerà tutti loro e raccoglierà tutto il risposte (ad esempio somma di punti).

Questa è la mia classe di base:

public abstract class BaseClass 
{ // all same attributes and methods 
    public long GetPoint(int nationalCode); 
} 

Per ciascuno dei sub Aziende Servizi web, implemento una classe che eredita questa classe di base e definire il proprio metodo GetPoint.

public class Company1 
{ 
    //implement own GetPoint method (call a web service). 
} 

a

public class CompanyN 
{ 
    //implement own GetPoint method (call a web service). 
} 

così, questo è il mio metodo web:

 [WebMethod] 
     public long MyCollector(string nationalCode) 
     { 

      BaseClass[] Clients = new BaseClass[] { new Company1(),//... ,new Company1()} 

      long Result = 0; 
      foreach (var item in Clients) 
      { 
       long ResultTemp = item.GetPoint(nationalCode); 
       Result += ResultTemp; 
      } 
     return Result; 
     } 

OK, funziona ma è così lento, perché ogni sub companys servizio web è ospitato su server diversi (su internet).

posso usare programmazione parallela in questo modo: (è questo chiamato programmazione parallela !?)

foreach (var item in Clients) 
    { 
        Tasks.Add(Task.Run(() => 
         { 
         Result.AddRange(item.GetPoint(MasterLogId, mobileNumber));     
        } 
     } 

Penso programmazione parallela (e filettatura) non è buono per questa soluzione, perché la mia soluzione è legata IO (non ad alta intensità di CPU)!

Chiama ogni servizio Web esterno è così lento, ho ragione? Molti thread che sono in sospeso per ottenere risposta!

Penso che la programmazione asincrona sia la migliore, ma io sono nuovo alla programmazione asincrona e alla programmazione parallela.

Qual è il modo migliore? (parallel.foreach - async TAP - APM asincrono - asincrono EAP -threading)

Si prega di scrivere per me un esempio.

+0

Hai tutte le nozioni di base giuste. È un buon inizio. 'Parallel.ForEach' e' Thread's soffrono dello stesso problema di 'Task.Run' - sono utili per la CPU, non per il lavoro legato all'IO. TAP è sicuramente il più facile da comporre (in pratica si mantiene il loop corrente, ma la roba di I/O diventa "senza fili" e molto scalabile). APM ed EAP sono facilmente convertibili in TAP (tramite 'Task.Factory.FromAsync' per APM e' TaskCompletionSource ' per EAP), quindi puoi andare con TAP e collegare le chiamate APM ed EAP se questo è ciò che forniscono le API esistenti. –

risposta

7

È bello vedere qualcuno che ha fatto i compiti.

Per prima cosa, da .NET 4 (e questo è ancora oggi il caso) TAP è la tecnologia preferita per il flusso di lavoro asincrono in .NET. Le attività sono facilmente componibili e, parallelamente, le chiamate al servizio Web sono semplicissime se forniscono autentiche API Task<T> -returning.Per ora lo hai "falsificato" con Task.Run, e per il momento potrebbe essere sufficiente per i tuoi scopi. Certo, i thread del thread thread passeranno un sacco di tempo a bloccarsi, ma se il carico del server non è molto alto si potrebbe benissimo farla franca anche se non è la cosa ideale da fare.

Hai solo bisogno di correggere una potenziale condizione di gara nel tuo codice (più su quello verso la fine).

Se si desidera seguire le migliori pratiche, si va con true TAP. Se le tue API forniscono Task - metodi di restituzione immediati, è facile. In caso contrario, non è game over poiché APM ed EAP possono essere facilmente convertiti in TAP. Riferimento MSDN: https://msdn.microsoft.com/en-us/library/hh873178(v=vs.110).aspx

Includerò anche alcuni esempi di conversione qui.

APM (preso da un'altra domanda SO):

MessageQueue non fornisce un metodo ReceiveAsync, ma possiamo farlo giocare a palla con Task.Factory.FromAsync:

public static Task<Message> ReceiveAsync(this MessageQueue messageQueue) 
{ 
    return Task.Factory.FromAsync(messageQueue.BeginReceive(), messageQueue.EndPeek); 
} 

... 

Message message = await messageQueue.ReceiveAsync().ConfigureAwait(false); 

Se i proxy del servizio web avere metodi BeginXXX/EndXXX, questa è la strada da percorrere.

EAP

Si supponga di avere un vecchio proxy del servizio Web derivato da SoapHttpClientProtocol, con solo i metodi asincroni basati su eventi. È possibile convertire loro di TAP come segue:

public Task<long> GetPointAsyncTask(this PointWebService webService, int nationalCode) 
{ 
    TaskCompletionSource<long> tcs = new TaskCompletionSource<long>(); 

    webService.GetPointAsyncCompleted += (s, e) => 
    { 
     if (e.Cancelled) 
     { 
      tcs.SetCanceled(); 
     } 
     else if (e.Error != null) 
     { 
      tcs.SetException(e.Error); 
     } 
     else 
     { 
      tcs.SetResult(e.Result); 
     } 
    }; 

    webService.GetPointAsync(nationalCode); 

    return tcs.Task; 
} 

... 

using (PointWebService service = new PointWebService()) 
{ 
    long point = await service.GetPointAsyncTask(123).ConfigureAwait(false); 
} 

Evitare gare dell'aggregazione dei risultati

per quanto riguarda l'aggregazione dei risultati paralleli, il codice ciclo TAP è quasi destra, ma è necessario evitare di mutanti stato condiviso all'interno dei vostri corpi Task poiché potrebbero essere eseguiti in parallelo. Lo stato condiviso è Result nel tuo caso, che è una sorta di raccolta. Se questa raccolta non è thread-safe (vale a dire se è una semplice List<long>), allora hai una condizione di competizione e potresti ottenere eccezioni e/o risultati scartati su Add (sto assumendo che nel tuo codice fosse un errore di battitura, ma in caso contrario, quanto sopra si applica ancora).

Un semplice riscrittura async-friendly che risolve la vostra razza sarebbe simile a questa:

List<Task<long>> tasks = new List<Task<long>>(); 

foreach (BaseClass item in Clients) { 
    tasks.Add(item.GetPointAsync(MasterLogId, mobileNumber));     
} 

long[] results = await Task.WhenAll(tasks).ConfigureAwait(false); 

Se si decide di essere pigro e bastone con la soluzione Task.Run per ora, la versione corretta sarà simile a questa:

List<Task<long>> tasks = new List<Task<long>>(); 

foreach (BaseClass item in Clients) 
{ 
    Task<long> dodgyThreadPoolTask = Task.Run(
     () => item.GetPoint(MasterLogId, mobileNumber) 
    ); 

    tasks.Add(dodgyThreadPoolTask);     
} 

long[] results = await Task.WhenAll(tasks).ConfigureAwait(false); 
2

È possibile creare una versione asincrona del GetPoint:

public abstract class BaseClass 
{ // all same attributes and methods 
    public abstract long GetPoint(int nationalCode); 

    public async Task<long> GetPointAsync(int nationalCode) 
    { 
     return await GetPoint(nationalCode); 
    } 
} 

Poi, raccogliere i compiti per ogni chiamata cliente. Successivamente, eseguire tutte le attività utilizzando Task.WhenAll. Questo li eseguirà tutti in parallelo. Inoltre, come sottolineato da Kirill, è possibile attendere i risultati di ogni attività:

var tasks = Clients.Select(x => x.GetPointAsync(nationalCode)); 
long[] results = await Task.WhenAll(tasks); 

Se non si vuole fare il metodo di aggregazione asincrono, è possibile raccogliere i risultati chiamando .Result invece di attesa, come so:

long[] results = Task.WhenAll(tasks).Result; 
+1

Quello che volevo dire era 'var results = attende Task.WhenAll (tasks);' - sorry, dovrei essere più chiaro. –

Problemi correlati