2016-01-06 15 views
10

Mam 30 podsieci i każdy z nich wdrożył ich serwis internetowy (z różnymi technologiami).Najlepszy sposób na połączenie się z wieloma usługami sieciowymi?

Muszę zaimplementować usługę sieciową do ich agregacji, na przykład wszystkie usługi sieciowe podsieci mają metodę sieci o nazwie GetUserPoint(int nationalCode) i muszę wdrożyć moją usługę sieciową, która będzie wywoływać je wszystkie i zbierać wszystkie odpowiedzi (na przykład suma punktów).

To moja klasa bazowa:

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

Dla każdego z sub firmy usług internetowych, zaimplementować klasę, która dziedziczy tę klasę bazową i zdefiniować swoją własną metodę GetPoint.

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

do

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

tak, to jest moja metoda internetowej:

 [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, to działa, ale to jest tak powolny, ponieważ każdy Companys sub serwis internetowy jest hostowany na różnych serwerach (w Internecie).

mogę używać równoległego programowanie tak: (jest to nazywane programowanie równoległe !?)

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

myślę programowanie równoległe (i gwintowania) nie jest dobre dla tego rozwiązania, ponieważ moje rozwiązanie jest związany IO (nie intensywny procesor)!

Zadzwoń do każdej zewnętrznej usługi internetowej jest tak powolny, mam rację? Wiele wątków czekających na odpowiedź!

Myślę, że programowanie asynchroniczne jest najlepszym sposobem, ale jestem nowy w programowaniu asynchronicznym i programowaniu równoległym.

Jaki jest najlepszy sposób? (parallel.foreach - asynchroniczny TAP - asynchroniczny APM - asynchroniczny wątek EAP)

Proszę napisać dla mnie przykład.

+0

Masz wszystkie podstawy w porządku. To całkiem dobry początek. 'Parallel.ForEach' i' Thread's cierpią z powodu tego samego problemu co 'Task.Run' - są dobre dla CPU, a nie dla IO-bound. TAP jest zdecydowanie najłatwiejszy do skomponowania (zasadniczo zachowujesz bieżącą pętlę, ale elementy IO stają się "bez wątków" i bardzo skalowalne). APM i EAP można łatwo przekonwertować do TAP (za pośrednictwem 'Task.Factory.FromAsync' dla APM i' TaskCompletionSource 'dla EAP), więc możesz użyć TAP i podłączyć APM i wywołania EAP, jeśli to zapewniają istniejące API. –

Odpowiedz

7

To odświeżające widzieć kogoś, kto odrobił pracę domową.

Po pierwsze, od wersji .NET 4 (i tak jest w dużej mierze dzisiaj) TAP jest preferowaną technologią asynchronicznego przepływu pracy w .NET. Zadania można łatwo komponować, a wykonywanie połączeń z usługami sieci Web jest proste, jeśli zapewniają prawdziwe Task<T> - zwracanie interfejsów API.Na razie "sfałszowałeś" go pod Task.Run, a na razie może to dobrze wystarczyć dla twoich celów. Z pewnością wątki puli wątków spędzają dużo czasu na blokowaniu, ale jeśli obciążenie serwera nie jest zbyt wysokie, można by z niego uciec, nawet jeśli nie jest to idealne rozwiązanie.

Wystarczy, że naprawisz potencjalny stan wyścigu w swoim kodzie (więcej na ten temat pod koniec).

Jeśli jednak chcesz zastosować najlepsze praktyki, skorzystaj z prawdziwego TAP. Jeśli twoje interfejsy API dostarczają Task - prostego wyciągania metod z pudełka, to proste. Jeśli nie, to nie koniec gry, ponieważ APM i EAP można łatwo przekonwertować na TAP. Numer referencyjny MSDN: https://msdn.microsoft.com/en-us/library/hh873178(v=vs.110).aspx

Podam tu również przykłady konwersji.

APM (wzięte z innego SO pytanie):

MessageQueue nie przewiduje metodę ReceiveAsync, ale możemy zmusić go do gry w piłkę poprzez 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); 

Jeśli Twoje proxy usług internetowych mieć metody BeginXXX/EndXXX, to jest droga.

EAP

Załóżmy masz stary proxy usług internetowych pochodzący z SoapHttpClientProtocol, tylko z metod asynchronicznych zdarzeń oparte. Można konwertować je do TAP następująco:

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

Unikanie wyścigów podczas agregowania wyników

w odniesieniu do agregowania równoległe wyniki, kod pętli TAP jest prawie rację, ale trzeba unikać mutowania stan współdzielony w waszych jednostkach Task, ponieważ prawdopodobnie będą wykonywane równolegle. Współdzielony stan to Result w twoim przypadku - który jest pewnego rodzaju zbiorem. Jeśli ta kolekcja nie jest bezpieczna dla wątków (np. Jeśli jest prosta List<long>), to masz warunki wyścigu i możesz uzyskać wyjątki i/lub odrzucone wyniki na Add (Przypuszczam, że w Twoim kodzie był AddRange to literówka, ale jeśli nie - powyższe nadal obowiązuje).

Prosty asynchroniczny przyjazne przepisać że naprawia swój wyścig będzie wyglądać następująco:

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

Jeśli zdecydujesz się być leniwy i trzymać się Task.Run roztworu do teraz, wersja poprawiona będzie wyglądać następująco:

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

Można utworzyć wersję asynchronicznej z 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); 
    } 
} 

Następnie zebrać zadania dla każdego połączenia klienta. Następnie wykonaj wszystkie zadania, używając Task.WhenAll. Spowoduje to wykonanie ich wszystkich równolegle. Ponadto, jak wskazał Cyryl, można poczekać na wyniki każdego zadania:

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

Jeśli nie chcesz, aby metody agregowania asynchronicznie, można zbierać wyniki wywołując .Result zamiast czekając, jak więc:

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

Co miałem na myśli, to 'var results = await Task.WhenAll (zadania);' - przepraszam, powinienem był jaśniejszy. –

Powiązane problemy