12

Używam apache http klienta w ciągu wiosny MVC 3.2.2 aby wysłać 5 żądań GET synchronicznie zgodnie z rysunkiem.Jak wysłać równoległe żądania GET i czekać na odpowiedzi wyników?

Jak mogę wysłać wszystkie te asynchronicznie (równolegle) i poczekać na wnioski, aby powrócić w celu powrotu do analizowany ciąg ładunku ze wszystkich żądań GET?

public String myMVCControllerGETdataMethod() 
{ 
    // Send 1st request 
    HttpClient httpclient = new DefaultHttpClient(); 
    HttpGet httpget = new HttpGet("http://api/data?type=1"); 
    ResponseHandler<String> responseHandler = new BasicResponseHandler(); 
    String responseBody = httpclient.execute(httpget, responseHandler); 

    // Send 2st request 
    HttpClient httpclient2 = new DefaultHttpClient(); 
    HttpGet httpget2 = new HttpGet("http://api/data?type=2"); 
    ResponseHandler2<String> responseHandler2 = new BasicResponseHandler(); 
    String responseBody2 = httpclient.execute(httpget, responseHandler2); 

    // o o o more gets here 

    // Perform some work here...and wait for all requests to return 
    // Parse info out of multiple requests and return 
    String results = doWorkwithMultipleDataReturned(); 

    model.addAttribute(results, results); 
    return "index"; 

} 

Odpowiedz

10

Tak w ogóle, trzeba ująć swoje jednostki pracy w Runnable lub java.util.concurrent.Callable i realizują je poprzez java.util.concurrent.Executor (lub org.springframework.core.task.TaskExecutor). Dzięki temu każda jednostka pracy może być wykonywana oddzielnie, zazwyczaj w sposób asynchroniczny (w zależności od implementacji Executor).

Więc dla danego problemu, można zrobić coś takiego:

import java.util.ArrayList; 
import java.util.Iterator; 
import java.util.List; 
import java.util.concurrent.Callable; 
import java.util.concurrent.Executor; 
import java.util.concurrent.FutureTask; 
import org.apache.http.client.methods.HttpGet; 
import org.apache.http.impl.client.BasicResponseHandler; 
import org.apache.http.impl.client.DefaultHttpClient; 
import org.springframework.stereotype.Controller; 
import org.springframework.ui.Model; 
import org.springframework.web.bind.annotation.RequestMapping; 

@Controller 
public class MyController { 
    //inject this 
    private Executor executor; 

    @RequestMapping("/your/path/here") 
    public String myMVCControllerGETdataMethod(Model model) { 
     //define all async requests and give them to injected Executor 
     List<GetRequestTask> tasks = new ArrayList<GetRequestTask>(); 
     tasks.add(new GetRequestTask("http://api/data?type=1", this.executor)); 
     tasks.add(new GetRequestTask("http://api/data?type=2", this.executor)); 
     //... 
     //do other work here 
     //... 
     //now wait for all async tasks to complete 
     while(!tasks.isEmpty()) { 
      for(Iterator<GetRequestTask> it = tasks.iterator(); it.hasNext();) { 
       GetRequestTask task = it.next(); 
       if(task.isDone()) { 
        String request = task.getRequest(); 
        String response = task.getResponse(); 
        //PUT YOUR CODE HERE 
        //possibly aggregate request and response in Map<String,String> 
        //or do something else with request and response 
        it.remove(); 
       } 
      } 
      //avoid tight loop in "main" thread 
      if(!tasks.isEmpty()) Thread.sleep(100); 
     } 
     //now you have all responses for all async requests 

     //the following from your original code 
     //note: you should probably pass the responses from above 
     //to this next method (to keep your controller stateless) 
     String results = doWorkwithMultipleDataReturned(); 
     model.addAttribute(results, results); 
     return "index"; 
    } 

    //abstraction to wrap Callable and Future 
    class GetRequestTask { 
     private GetRequestWork work; 
     private FutureTask<String> task; 
     public GetRequestTask(String url, Executor executor) { 
      this.work = new GetRequestWork(url); 
      this.task = new FutureTask<String>(work); 
      executor.execute(this.task); 
     } 
     public String getRequest() { 
      return this.work.getUrl(); 
     } 
     public boolean isDone() { 
      return this.task.isDone(); 
     } 
     public String getResponse() { 
      try { 
       return this.task.get(); 
      } catch(Exception e) { 
       throw new RuntimeException(e); 
      } 
     } 
    } 

    //Callable representing actual HTTP GET request 
    class GetRequestWork implements Callable<String> { 
     private final String url; 
     public GetRequestWork(String url) { 
      this.url = url; 
     } 
     public String getUrl() { 
      return this.url; 
     } 
     public String call() throws Exception { 
      return new DefaultHttpClient().execute(new HttpGet(getUrl()), new BasicResponseHandler()); 
     } 
    } 
} 

Zauważ, że ten kod nie został przetestowany.

Dla swojej implementacji Executor, sprawdź Spring's TaskExecutor i task:executor namespace. Prawdopodobnie potrzebujesz puli wątków wielokrotnego użytku dla tego przypadku użycia (zamiast tworzenia nowego wątku za każdym razem).

+0

Ah, bardzo fajnie! Poddam temu jazdę próbną. Dzięki! Mam jednak jedno pytanie, skąd mam wiedzieć, która odpowiedź jest w pętli iteracyjnej? Co to znaczy, że mój kontroler bezstanowy przekazuje wyniki do metody doWorkwithMultipleDataReturned()? – JaJ

+0

Przykładowy kod umożliwia dopasowanie oryginalnego żądania (adresu URL) do odpowiedzi za pomocą abstrakcji "GetRequestTask". Tak więc w wierszu '// UDOSTĘPNIJ TWOJ KOD TUTAJ 'masz już oba jako łańcuchy. O bezstanowym komentarzu, ja zakładając, ponieważ metoda 'doWorkwithMultipleDataReturned' nie miała żadnych argumentów, które mogłyby trzymania odpowiedzi w zmiennej instancji kontrolera, dzięki czemu kontroler Stateful (ograniczenie korzystania z tej samej instancji w wielu wątków) . Zamiast tego powinieneś zachować odniesienia do odpowiedzi tylko jako zmienne metody, aby uniknąć tego problemu. – superEb

+0

Świetne punkty! Jeszcze raz dziękuję za informacje! – JaJ

11

Należy użyć AsyncHttpClient. Możesz wykonać dowolną liczbę próśb, a ona oddzwoni do Ciebie, gdy otrzyma odpowiedź. Możesz skonfigurować liczbę połączeń, które może utworzyć. Wszystkie wątki są obsługiwane przez bibliotekę, więc jest to znacznie łatwiejsze niż samodzielne zarządzanie wątkami.

spójrz na przykład tutaj: https://github.com/AsyncHttpClient/async-http-client

+0

niesamowite! doceń informacje! – JaJ

+0

nadzieję, że to pomaga! jest solidny jak skała. używamy go do wszystkich naszych połączeń http do usług zewnętrznych. –

+0

Tak, wolałbym użyć asynchroniczny tutaj, ponieważ nawet przy użyciu wątków z zarządzanej puli będzie blokować przychodzące żądania HTTP, ponieważ będą one związać się o-> I/clr/JVM. Jedyny problem, jaki widzę, to metoda, z której ją wywołuję, to kontroler mvc na wiosnę. Tak więc nie jestem pewien, w jaki sposób mogę użyć wywołania zwrotnego, aby ponownie połączyć się z tym samym widokiem. To "powrót z powrotem. Aplikacja jest aplikacją internetową i jest używana jako interfejs użytkownika. – JaJ

Powiązane problemy