Search code examples
javahttpclient

Can I run multiple HttpAsyncClients with one PoolingNHttpClientConnectionManager?


I want to create multiple HttpAsyncClient with a single connection manager (similar to the the example in the Baeldung article).

The problem is that this does not seem to work with the HttpAsyncClient (only with the HttpClient). The application fails with the following errors in the log:

14:00:56.443 [pool-2-thread-1] ERROR org.apache.http.impl.nio.client.InternalHttpAsyncClient - I/O reactor terminated abnormally
java.lang.IllegalStateException: Illegal state ACTIVE
    at org.apache.http.util.Asserts.check(Asserts.java:46)
    at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:316)
    at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.execute(PoolingNHttpClientConnectionManager.java:221)
    at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase$1.run(CloseableHttpAsyncClientBase.java:64)
    at java.base/java.lang.Thread.run(Thread.java:833)

Exception in thread "main" java.util.concurrent.ExecutionException: java.util.concurrent.CancellationException: Request execution cancelled
    at org.apache.http.concurrent.BasicFuture.getResult(BasicFuture.java:71)
    at org.apache.http.concurrent.BasicFuture.get(BasicFuture.java:84)
    at org.apache.http.impl.nio.client.FutureWrapper.get(FutureWrapper.java:70)
    at com.example.Test.main(Test.java:32)
Caused by: java.util.concurrent.CancellationException: Request execution cancelled
    at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase.execute(CloseableHttpAsyncClientBase.java:114)
    at org.apache.http.impl.nio.client.InternalHttpAsyncClient.execute(InternalHttpAsyncClient.java:138)
    at org.apache.http.impl.nio.client.CloseableHttpAsyncClient.execute(CloseableHttpAsyncClient.java:75)
    at org.apache.http.impl.nio.client.CloseableHttpAsyncClient.execute(CloseableHttpAsyncClient.java:108)
    at org.apache.http.impl.nio.client.CloseableHttpAsyncClient.execute(CloseableHttpAsyncClient.java:92)
    at com.example.Test.main(Test.java:31)

This is the code I am running:

package com.example;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;

import java.util.concurrent.ExecutionException;

public class Test {
    public static void main(String[] args) throws IOReactorException, ExecutionException, InterruptedException {
        ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(IOReactorConfig.custom().build());
        PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(ioReactor);
        CloseableHttpAsyncClient client1 = HttpAsyncClientBuilder.create()
                .setConnectionManager(connManager)
                .setRedirectStrategy(new LaxRedirectStrategy())
                .build();
        client1.start();
        CloseableHttpAsyncClient client2 = HttpAsyncClientBuilder.create()
                .setConnectionManager(connManager)
                .build();
        client2.start();

        var res1 = client1.execute(new HttpGet("https://www.google.com"), null);
        res1.get();
        var res2 = client2.execute(new HttpGet("https://www.google.com"), null);
        res2.get();
    }
}

I use the version 4.4.15 of https://hc.apache.org/


Solution

  • Tell both builders that the connection manager is shared:

    CloseableHttpAsyncClient client1 = HttpAsyncClientBuilder.create()
            .setConnectionManager(connManager)
            .setRedirectStrategy(new LaxRedirectStrategy())
            .build();
    client1.start();
    CloseableHttpAsyncClient client2 = HttpAsyncClientBuilder.create()
            .setConnectionManager(connManager)
            // do not call setConnectionManagerShared on the first client, only on all others sharing the same connManager
            .setConnectionManagerShared(true) 
            .build();
    client2.start();