How to call multiple rest api parallel in Java?
Asked Answered
P

1

6

What is the best way to call multiple api parallel in Java? I want to do multiple rest calls, combine the results and return a jsonArray. I am using ExecutorService and get the correct output when i access the url from only one client pc. But when i access the url from multiple clients, each time i am getting a jsonArray of different size even though i am calling same url.

What i have done is given below:

ExecutorService executor = Executors.newFixedThreadPool(5); 
resultArray = new JSONArray();
for (Branch branch : Branches) {
    Future<Response> response = executor.submit(new Request(branch.getUrl(), 
      branch.getUserName(), branch.getPassword()));
    responseBody = response.get().getResponseBody();
    resultArray.put(responseBody);
}
executor.shutdown();
while(!executor.isTerminated()) {

}
return resultArray.toString();


public class Request implements Callable<Response> {

private HttpURLConnection con;
private URL obj;
private String response;

private String url;
private String username;
private String password;

public Request(String url, String username, String password) {
    this.url = url;
    this.username = username;
    this.password = password;
}

@Override
public Response call() {
    try {
        obj = new URL(url);
        con = (HttpURLConnection) obj.openConnection();
        String userCredentials = username + ":" + password;
        String basicAuth = "Basic " + java.util.Base64.getEncoder().encodeToString(userCredentials.getBytes());
        con.setRequestProperty ("Authorization", basicAuth);
        con.setRequestMethod("GET");

        int responseCode = con.getResponseCode();
        if(responseCode == 200) {
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer stringBuffer = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                stringBuffer.append(inputLine);
            }
            in.close();
            response = stringBuffer.toString();
            return new Response(responseCode, response);
        }
        else {
            response = "{\"response\":\"some error occurred\"}";
            return new Response(responseCode, response);
        }

    } catch (IOException e) {
        response = "{\"output\":\"some error occurred\"}";
        return new Response(404, response);
    }
}
}

public class Response {

private String responseBody;
private int responseCode;

public Response(int responseCode, String responseBody) {
    this.responseBody = responseBody;
    this.responseCode = responseCode;
}

public int getResponseCode() {
    return responseCode;
}

public String getResponseBody() {
    return responseBody;
}
}
Petra answered 15/5, 2018 at 8:49 Comment(5)
You are not actually doing calls in parallel if you call get on the first callable before submitting the second one. You need to submit all of them and call get on the resulting list of Futures afterwards.Lief
If you get different/wrong results when you concurrently hit the URL from multiple machines, then this sounds like a server-side problem, not a client-side issue.Lief
well, i am new to java, could you please suggest some reference for achieving my goal?Petra
I think you need to show the server-side code.Lief
this is only the server side code, i am calling rest apis (like railway api) and need do combine results from apis. means my app works as a middle-warePetra
S
9

Please check this,

Collection<Callable<Response>> tasks = new ArrayList<>();
for (Branch branch : Branches) {
    tasks.add(new Request(branch.getUrl(), branch.getUserName(), branch.getPassword()));
}

int numThreads = Branches.size() > 4 ? 4 : Branches.size();
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
List<Future<Response>> results = executor.invokeAll(tasks);
for(Future<Response> response : results){
    responseBody = response.get().getResponseBody();
    resultArray.put(responseBody);
}

Java 7 onwards we you can try replace ExecutorService with ForkJoin Pool,

Collection<Callable<Response>> tasks = new ArrayList<>();
for (Branch branch : Branches) {
    tasks.add(new Request(branch.getUrl(), branch.getUserName(), branch.getPassword()));
}

int numThreads = Branches.size() > 4 ? 4 : Branches.size();
ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
List<Future<Response>> results = pool.invokeAll(tasks);
for(Future<Response> response : results){
    responseBody = response.get().getResponseBody();
    resultArray.put(responseBody);
}

Note:- The code is not tested, i have used something like this few months back.

Scarper answered 16/9, 2018 at 16:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.