![](/img/trans.png)
[英]How to send requests parallel to asynchronous services and collect the responses in a Java EE application?
[英]How to send parallel GET requests and wait for result responses?
我在spring mvc 3.2.2中使用apache http client来同步发送5个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";
}
通常,您需要将您的工作单元封装在Runnable
或java.util.concurrent.Callable
,并通过java.util.concurrent.Executor
(或org.springframework.core.task.TaskExecutor
)执行它们。 这允许每个工作单元通常以异步方式(取决于Executor
的实现)单独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的TaskExecutor和task:executor namespace 。 您可能需要一个可重用的线程池用于此用例(而不是每次都创建一个新线程)。
您应该使用AsyncHttpClient。 您可以发出任意数量的请求,并在收到回复时回复您。 您可以配置它可以创建的连接数。 所有线程都由库处理,因此它比自己管理线程更容易。
看看这里的例子: https : //github.com/AsyncHttpClient/async-http-client
用于与单个HttpClient实例并行执行多个请求。
配置PoolingHttpClientConnectionManager以进行并行执行。
HttpClientBuilder builder = HttpClientBuilder.create();
PlainConnectionSocketFactory plainConnectionSocketFactory = new PlainConnectionSocketFactory();
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainConnectionSocketFactory).build();
PoolingHttpClientConnectionManager ccm = new PoolingHttpClientConnectionManager(registry);
ccm.setMaxTotal(BaseConstant.CONNECTION_POOL_SIZE); // For Example : CONNECTION_POOL_SIZE = 10 for 10 thread parallel execution
ccm.setDefaultMaxPerRoute(BaseConstant.CONNECTION_POOL_SIZE);
builder.setConnectionManager((HttpClientConnectionManager) ccm);
HttpClient objHttpClient = builder.build();
将您的请求代码移动到单独的方法:
private String executeGet(String url){
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
return httpclient.execute(httpget, responseHandler);
}
并将它们提交给ExecutorService
:
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> firstCallFuture = executorService.submit(() -> executeGet(url1));
Future<String> secondCallFuture = executorService.submit(() -> executeGet(url2));
String firstResponse = firstCallFuture.get();
String secondResponse = secondCallFuture.get();
executorService.shutdown();
要么
Future<String> firstCallFuture = CompletableFuture.supplyAsync(() -> executeGet(url1));
Future<String> secondCallFuture = CompletableFuture.supplyAsync(() -> executeGet(url2));
String firstResponse = firstCallFuture.get();
String secondResponse = secondCallFuture.get();
或者如何使用Spring WebClient同时进行多个调用中所述使用RestTemplate ?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.