How to Send Parallel Get Requests and Wait for Result Responses

How to send parallel GET requests and wait for result responses?

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).

How can I make multiple http request in parallel and process each of the response independently?

You're right about Promise.all, that is intended for waiting until all of a set of Promises have all resolved. It creates a new Promise that resolves only once all the Promises used to create it have resolved, then passes an array of the values they resolved with to the "all" Promise. You can still make requests in parallel, but this function is for when you want to wait for them all to resolve before handling the result.

If you just need to handle each Promise as it resolves, then you can just bind a callback to them and send off all your requests. As each Promise resolves, its handler will be executed. No special extra code is required.

A simple approach like this will let you make multiple requests and handle each one as its Promise resolves.

fetch(url1).then(handleRequest1).catch(handleError1);
fetch(url2).then(handleRequest2).catch(handleError2);
fetch(url3).then(handleRequest3).catch(handleError3);

Apache Camel: How to send two http requests in parallel and wait for the responses?

Try to split You route into many smaller routes. Then You can perform necessary unmarshalling there.

See question about unmarshalling http response

from("direct:getContact")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
CamelContext context = exchange.getContext();
ProducerTemplate producerTemplate = context.createProducerTemplate();

// Asynchronous call to internal route
Future<Contact> contact =
producerTemplate.asyncRequestBody("direct:invokeSomeRestApi", null, Contact.class);

// Do rest of the work
exchange.getOut().setBody(contact.get());
}
});

// Unmarshalling REST response
JacksonDataFormat jacksonDataFormat = new JacksonDataFormat();
jacksonDataFormat.setUnmarshalType(Contact.class);

// Internal route definition
from("direct:invokeSomeRestApi")
.to("http://localhost:8080/api/contact/2345")
.unmarshal(jacksonDataFormat);

How to send multiple of asynchronous requests from client to server

Asynchronous means sending message and not waiting for response.

            //Get the response message from server - so you wait whole 5 seconds for response :)
String responseMessage = reader.readLine();

The simplest solution in this case is to remove waiting for response each time. So, get rid of above lines from loop inside Client class.

In this particular client-sever case you do not need additional threads, if you would perform asynchronous things inside one application, then so. Take a look at Java Futures with some tutorial on how to use them.
But if you want to get a result from server, you have to wait anyway. And you want to get results of all calcuations. Hence, you have to store all incoming requests somewhere. Simple, naive and impractical, but showing asynchronicity concept code may look like this

public class Client {
public static void main(String[] args) throws IOException {
try {
long start = System.currentTimeMillis();
BufferedReader reader = null;
for(int i = 0 ; i < 20; i++){
Socket s = new Socket("localhost", 7777);
PrintWriter writer = new PrintWriter(s.getOutputStream());
InputStreamReader streamReader = new InputStreamReader(s.getInputStream());
reader = new BufferedReader(streamReader);
// just make sure you send data and do not wait for response
System.out.println("Sending " + i + " : " + (System.currentTimeMillis() - start));
writer.println(i);
writer.flush();
}
//this line works like future.get(), it hangs client until it receives result
String responseMessage = reader.readLine();
// process returned data as you want
System.out.println(responseMessage);
}
catch (IOException ex) {
ex.printStackTrace(); // (**)
}
}
}

public class Server {
public static void main(String[] args) throws IOException {
try {
//Make a ServerSocket to listen for message
ServerSocket ss = new ServerSocket(7777);
Socket s;
//we need to store incomming requests to process, and return them
List<String> integerList = new LinkedList<>();
while (true) {
s = ss.accept();

InputStreamReader streamReader = new InputStreamReader(s.getInputStream());
BufferedReader reader = new BufferedReader(streamReader);
String message = reader.readLine();
System.out.println(message);
// do something here

Thread.sleep(5000);

PrintWriter writer = new PrintWriter(s.getOutputStream());
integerList.add(message);
writer.println(integerList);
writer.close();
}
} catch (IOException | InterruptedException ex) {
System.out.println(ex);
}
}
}

How to call multiple http requests parallel and handle errors individually

You need to combine all observables from your service into a single observable and then subscribe to the newly created observable. to do this you can use RxJs operator forkJoin

the fork join subscription will wait for all observables to complete and then emit the values to the subscription method -note that mean single success and single error for all since the observable will only emit once

you can also use RxJs Operator combineLatest
this will create single observable from your list of observers and emit a value every time one of the observables complete. the value will be array of all last values emmited from your observers or null in case an observer not finished. this will allow you to handle error state for each api call but will also fire the subscription event multiple times.

Java best way to send multiple HTTP requests

If all your API calls are independent then you can fire first, then you can join the results later.

Simple example with GET:

List<URI> uris  = ... //

HttpClient client = HttpClient.newHttpClient();
List<HttpRequest> requests = uris.stream()
.map(HttpRequest::newBuilder)
.map(reqBuilder -> reqBuilder.build())
.collect(toList());

CompletableFuture.allOf(requests.stream()
.map(request -> client.sendAsync(request, ofString()))
.toArray(CompletableFuture<?>[]::new))
.join();

You might benefit from using a custom executor as well:

private static final ExecutorService executorService = Executors.newFixedThreadPool(5);

private static final HttpClient httpClient = HttpClient.newBuilder()
.executor(executorService)
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();

Edited----

If you want results, then allOf does not help as it actually returns a VOID.

static CompletableFuture allOf(CompletableFuture<?>... cfs)

Returns a new CompletableFuture that is completed when all of the
given CompletableFutures complete.

Instead you can still use join, however in a different way:

List<CompleteFuture<HttpResponse<String>> listOfCompletableFutures = ...

listOfCompletableFutures.
.stream()
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(Collectors.toList());

Good references:

https://openjdk.java.net/groups/net/httpclient/recipes.html

Java 11 HttpClient - What is Optimum Ratio of HttpClients to Concurrent HttpRequests

https://mkyong.com/java/java-11-httpclient-examples/

Java collecting results of CompletableFuture from multiple calls

React send multiple requests in a loop and wait

You need to use await before async function if you want to wait for it for
finish

async function getAll(data) {
for(let i=0; i<= data.length; i+=10) {
await getSlice(data.slice(i,i+10));
}
}

getAll(ids).then(()=>console.log('all data processed')).catch(err=>/*handle
error*/)

Ps. i think that you need to use Promise.allSettled method instead of Promise.all. If one of your request will return an error, you will get all chunk failed if you will use Promise.all. Promise.allSettled will wait for all results - positive or negative.
Anothe old sollution is to use catch method for each request, like

 promises.push(fetch("myapi/"+key)
.then(res => res.json())
.then((result) => console.log(result))
.catch((err)=>{/*handle error if needed*/; return null})

And after that you will have some nulls in array with results



Related Topics



Leave a reply



Submit