12

私はspring mvc 3.2.2内でapache httpクライアントを使用して、図示のように5つのget要求を同期的に送信します。パラレルGETリクエストを送信し、結果レスポンスを待つ方法を教えてください。

これらをすべて非同期(パラレル)で送信し、すべての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"; 

} 

答えて

10

ただ、一般的に、あなたはRunnableまたはjava.util.concurrent.Callableに仕事のあなたのユニットをカプセル化し、java.util.concurrent.Executor(またはorg.springframework.core.task.TaskExecutor)を介してそれらを実行する必要があります。これにより、各作業単位を(通常は非同期で)別々に実行することができます(Executorの実装に依存します)。このコードはテストされていないことを

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

注:

は、だからあなたの特定の問題のために、あなたはこのような何かを行うことができます。

Executorの実装については、Spring's TaskExecutortask:executor namespaceをご確認ください。おそらく、毎回新しいスレッドを作成するのではなく、このユースケースのスレッドの再利用可能なプールが必要です。

+0

ああ、とてもクールです!私はこれに試乗をします。どうも!しかし、私が持っている1つの質問は、どのような応答が反復ループにあるのかをどのように知ることができるかです。また、私のコントローラがdoWorkwithMultipleDataReturned()メソッドに結果を渡すステートレス状態を維持することはどういう意味ですか? – JaJ

+0

サンプルコードでは、元のリクエスト(URL)を 'GetRequestTask'抽象化を介してレスポンスと照合することができます。ですから '' PUT YOUR CODE HERE'という行にはすでに両方とも文字列としてあります。 あなたのメソッド 'doWorkwithMultipleDataReturned'は、コントローラのインスタンス変数で応答を保持する引数を取らず、コントローラをステートフルにして(複数のスレッドに渡って同じインスタンスを使用することを制限する)、ステートレスなコメントについては、 。むしろ、その問題を避けるためには、レスポンスへの参照をメソッド変数として保持する必要があります。 – superEb

+0

大きなポイント!情報をもう一度ありがとう! – JaJ

11

AsyncHttpClientを使用する必要があります。任意の数のリクエストを行うことができ、応答を受け取ったときにコールバックします。作成できる接続の数を構成できます。すべてのスレッドはライブラリによって処理されるため、スレッドを自分で管理するよりも簡単です。

こちらの例をご覧ください:https://github.com/AsyncHttpClient/async-http-client

+0

すごい!情報をありがとう! – JaJ

+0

助けて欲しい!それは堅実です。我々はそれを外部サービスへのhttp呼び出しのすべてに使用します。 –

+0

はい、ここではasynchを使用します。管理されたプールからでもスレッドを使用すると、入ってくるhttpリクエストがブロックされるため、i/o-> clr/jvmを縛ります。私が見ている唯一の問題は、私がそれを呼び出す方法がスプリングMCUコントローラです。だから、コールバックを使って同じビューに再度コールバックする方法がわからない。それが「ドローバック」。このアプリはウェブアプリであり、ユーザーインターフェースとして使用されています。 – JaJ

関連する問題