javaasynchttpclient

How to use AsyncHttpClient to send multiple HTTP calls asynchronously?


I'm using the https://github.com/AsyncHttpClient/async-http-client to send an asynchronous POST request.

Example:

try {
        CompletableFuture<org.asynchttpclient.Response> whenResponse = asyncHttpClient()
            .preparePost("https://some-site.com/v1/subscription1")
            .setHeader("Content-Type","application/json")
            .setHeader("Accept", "application/json")
            .setBody(getData())
            .execute()
            .toCompletableFuture()
            .exceptionally(t -> {
              // handle error
            })
            .thenApply(
                response -> { return response; }
            );
        return whenResponse.join();
    } catch (Exception e) {
        // handle error
    }

Is it possible to refactor this to send the same data/body to multiple URLs asynchronously?

Preferably outlining the most efficient way (can loops be avoided here)?


Solution

  • First of all, when using asyncHttpClient() you should remember to close the AsyncHttpClient returned by that method once you are done sending your requests. Alternatively, you could define an AsyncHttpClient instance globally and reuse it for all your requests.

    Since you commented that CompletableFuture.allOf() does work for you, I assume that you're not interested in the results returned by the requests. If you are, your method could return a list of the individual CompletableFuture instances which you can safely inspect after calling join() on the instance created by allOf(). This join call would then have to be moved inside that method.

    Loops can clearly not be avoided unless you want to hardcode your URLs in which case you have to unroll the loop and create the array that is passed to allOf by hand.

    Here is an example of how you could implement the solution:

    import org.asynchttpclient.AsyncHttpClient;
    import org.asynchttpclient.Response;
    
    import java.util.List;
    import java.util.concurrent.CompletableFuture;
    import java.util.stream.Stream;
    
    import static org.asynchttpclient.Dsl.*;
    
    public final class Example {
        public static final AsyncHttpClient ASYNC_HTTP_CLIENT = asyncHttpClient();
    
        public static void main(String[] args) throws IOException {
            List<String> urls = List.of(
                    "https://some-site.com/v1/subscription1",
                    "https://some-site.com/v1/subscription2"
                    // etc.
            );
            CompletableFuture<Void> requests = sendRequests(urls);
            requests.join();
    
            ASYNC_HTTP_CLIENT.close();
        }
    
        public static CompletableFuture<Void> sendRequests(List<String> urls) {
            Stream<CompletableFuture<Response>> stream = urls.stream()
                    .map(url -> ASYNC_HTTP_CLIENT.preparePost(url)
                            .setHeader("Content-Type", "application/json")
                            .setHeader("Accept", "application/json")
                            .setBody(getData())
                            .execute()
                            .toCompletableFuture());
            return CompletableFuture.allOf(stream.toArray(CompletableFuture[]::new));
        }
    
        private static byte[] getData() {return new byte[0]; /* return actual data */}
    }