12

스프링 mvc 3.2.2에서 apache http 클라이언트를 사용하여 그림과 같이 get get requests를 동 기적으로 보냅니다.병렬 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

아, 아주 멋진! 나는 이것을 시험 운전에 줄 것이다. 고마워! 그러나 내가 가지고있는 한 가지 질문은 반복 루프에서 어떤 응답인지 어떻게 알 수 있습니까? 또한, 내 컨트롤러가 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

예, 관리되는 풀의 스레드를 사용하는 경우 들어오는 HTTP 요청이 I/O-> clr/jvm을 묶어 버리기 때문에 차단을 사용하기 때문에 여기서는 asynch를 사용합니다. 내가 볼 수있는 유일한 문제는 봄 mvc 컨트롤러에서 호출하는 메서드입니다. 그래서 콜백을 사용하여 동일한 뷰에 다시 콜백 할 수 있는지 잘 모르겠습니다. 그것 '다시 무승부. 이 앱은 웹 앱이며 사용자 인터페이스로 사용됩니다. – JaJ