12

Yaygın olarak gösterildiği gibi 5 alma isteği göndermek için spring mvc 3.2.2 içinde apache http istemcisini kullanıyorum.Paralel GET istekleri nasıl gönderilir ve sonuç yanıtları beklenir?

Tüm bu eşzamansız olarak (paralel olarak) nasıl gönderebilir ve tüm GET isteklerinden ayrıştırılmış bir yük dizesi döndürmek için isteklerin geri dönmesini bekleyebilir miyim? Sadece genel olarak

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

} 

cevap

10

, bir Runnable veya java.util.concurrent.Callable işin birimleri saklanması ve java.util.concurrent.Executor (veya org.springframework.core.task.TaskExecutor) üzerinden bunları yürütmek gerekiyor. Bu, her bir iş biriminin, genellikle eşzamansız bir şekilde (Executor'un uygulanmasına bağlı olarak) ayrı ayrı yürütülmesine izin verir. Bu kod test edilmediğini

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

Not:

Yani Özgül sorun, böyle bir şey yapabilirdi.

Executor uygulamanız için Spring's TaskExecutor ve task:executor namespace'a bakın. Muhtemelen bu kullanım durumu için yeniden kullanılabilir bir havuz havuzu (her seferinde yeni bir iplik oluşturmak yerine) istiyorsunuz.

+0

Ah, çok cool! Bunu bir test sürüşü yapacağım. Teşekkürler! Ancak, sahip olduğum bir soru, yinelemeli döngüde hangi cevabın olduğunu nasıl anlarım? Ayrıca, denetleyicinin vatansızlığının sonuçlarda doWorkwithMultipleDataReturned() yöntemine geçmesini sağlamak ne anlama geliyor? – JaJ

+0

Örnek kod, özgün isteği (URL) yanıtla birlikte 'GetRequestTask' soyutlamasıyla eşleştirmenizi sağlar. Yani '// PUT SENİN KODUNUZU BURADA' dizinizde zaten her iki dizge de var. Vatansız yorumu hakkında, “doWorkwithMultipleDataReturned” yönteminiz, denetleyicinizin örnek değişkeninde yanıtları tutabileceğiniz herhangi bir bağımsız değişken almadığından, varsayıcınızı varsayıyordum. Bu, denetleyicinizi durumsal hale getiriyor (aynı iş parçasının birden fazla iş parçacığı için kullanımını sınırlıyor) . Daha ziyade, bu problemden kaçınmak için yanıtları yalnızca yöntem değişkenleri olarak tutmalısınız. – superEb

+0

Harika puan! Bilgi için tekrar teşekkürler! – JaJ

11

AsyncHttpClient'i kullanmalısınız. İstediğiniz sayıda istekte bulunabilirsiniz ve yanıt alındığında size bir geri arama yapar. Ne kadar bağlantı oluşturabileceğini yapılandırabilirsiniz. Tüm iş parçacığı kitaplık tarafından işlenir, bu nedenle iş parçacıklarını kendiniz yönetmek daha kolaydır.

buraya bir göz bir örneği inceleyelim: https://github.com/AsyncHttpClient/async-http-client

+0

harika! Bilgiyi takdir et! – JaJ

+0

umarım yardımcı olur! bu kaya katı. Tüm http çağrılarımız için harici servislere kullanıyoruz. –

+0

Evet, burada asynch kullanmayı tercih ediyorum; çünkü yönetilen bir havuzdan bile olsa ileti dizileri kullanmak, gelen// clr/jvm'yi bağlayacağından gelen http isteklerini engeller. Gördüğüm tek sorun, onu çağırdığım yöntem bir yay mvc denetleyicisidir. Bu yüzden, tekrar aynı görüntüye geri aramak için geri arama özelliğini nasıl kullanabileceğimi bilmiyorum. Bu geri çekiliş. Uygulama bir web uygulaması ve Kullanıcı Arabirimi olarak kullanılıyor. – JaJ