I'm using apache http client within spring mvc 3.2.2 to send 5 get requests synchronously as illustrated.
How can I send all of these asynchronously (in parallel) and wait for the requests to return in order to return a parsed payload string from all GET requests?
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"; }
Code for concurrent API call: tags); // make concurrent api calls const requests = tags. map((tag) => axios. get("https://api.hatchways.io/assessment/blog/posts?tag=" + tag) ); try { // wait until all the api calls resolves const result = await Promise. all(requests); // posts are ready.
Just in general, you need to encapsulate your units of work in a Runnable
or java.util.concurrent.Callable
and execute them via java.util.concurrent.Executor
(or org.springframework.core.task.TaskExecutor
). This allows each unit of work to be executed separately, typically in an asynchronous fashion (depending on the implementation of the Executor
).
So for your specific problem, you could do something like this:
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()); } } }
Note that this code has not been tested.
For your Executor
implementation, check out Spring's TaskExecutor and task:executor namespace. You probably want a reusable pool of threads for this use-case (instead of creating a new thread every time).
You should use AsyncHttpClient. You can make any number of requests, and it will make a call back to you when it gets a response. You can configure how many connections it can create. All the threading is handled by the library, so it's alot easier than managing the threads yourself.
take a look a the example here: https://github.com/AsyncHttpClient/async-http-client
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With