Home > Blockchain >  External service call with Java 11 HttpClient sync vs async
External service call with Java 11 HttpClient sync vs async

Time:07-24

My microservice is calling an external service POST call and I want to use Java 11 Httpclient. Here how shall the send() and sendAsync() methods can make difference? I have tested with multiple amount of request, almost same latency. I tried executing 100 endpoint call for my service with 10 or 20 thread or more. The result for both methods are almost same.

I use sendAsync() with thenApply().get in response receive. I would like to know what is preferred way and why? Is using async is also fast(which is not as per my current result)?

Thanks in advance for your answers!

CodePudding user response:

Here's a test of both methods:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.Builder;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class HttpClientTest {
    static final int REQUEST_COUNT = 100;
    static final String URI_TEMPLATE = "https://jsonplaceholder.typicode.com/posts/%d";

    public static void main(final String[] args) throws Exception {
        final List<HttpRequest> requests = IntStream.rangeClosed(1, REQUEST_COUNT)
                .mapToObj(i -> String.format(URI_TEMPLATE, i))
                .map(URI::create)
                .map(HttpRequest::newBuilder)
                .map(Builder::build)
                .collect(Collectors.toList());
        final HttpClient client = HttpClient.newBuilder()
                .executor(Executors.newFixedThreadPool(REQUEST_COUNT))
                .build();
        final ThrowingFunction<HttpRequest, String> sendSync =
            request -> client.send(request, BodyHandlers.ofString()).body();
        final ThrowingFunction<CompletableFuture<HttpResponse<String>>, String> getSync =
            future -> future.get().body();
        benchmark("sync", () -> requests.stream()
                .map(sendSync)
                .collect(Collectors.toList()));
        benchmark("async", () -> requests.stream()
                .map(request -> client.sendAsync(request, BodyHandlers.ofString()))
                .collect(Collectors.toList()) // materialize to send the requests
                .stream()
                .map(getSync)
                .collect(Collectors.toList()));
    }

    static void benchmark(final String name, final Supplier<List<String>> supplier) {
        new Thread(() -> {
            final long start = System.nanoTime();
            System.out.printf("%s: start%n", name);
            final List<String> result = supplier.get();
            final Duration duration = Duration.ofNanos(System.nanoTime() - start);
            final int size = result.stream()
                    .mapToInt(String::length)
                    .sum();
            System.out.printf("%s: end, got %d chars, took %s%n", name, size, duration);
        }, name).start();
    }

    @FunctionalInterface
    static interface ThrowingFunction<T, R> extends Function<T, R> {
        default R apply(final T t) {
            try {
                return applyThrowing(t);
            } catch (final Exception e) {
                throw new RuntimeException(e);
            }
        }

        R applyThrowing(T t) throws Exception;
    }
}

Example output:

sync: start
async: start
async: end, got 26118 chars, took PT1.6102532S
sync: end, got 26118 chars, took PT4.3368509S

The higher the parallelism level of the API, the better the asynchronous method will perform.

  • Related