能否使用单个PoolingNHttpClientConnectionManager运行多个HttpAsyncClient?
问题:多个HttpAsyncClient共用连接管理器启动报错
尝试创建多个CloseableHttpAsyncClient实例并共用同一个PoolingNHttpClientConnectionManager时,运行出现以下错误:
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)
问题原因
HttpAsyncClient启动时,会自动调用连接管理器的execute()方法启动关联的IOReactor。但同一个IOReactor只能处于ACTIVE状态一次,第一个客户端启动后,IOReactor已经运行,第二个客户端尝试再次启动就会触发IllegalStateException,导致客户端初始化失败,请求被取消。
解决方案
方案1:复用单个HttpAsyncClient实例
CloseableHttpAsyncClient本身是线程安全的,完全可以在多线程环境下复用同一个实例处理不同请求,这是最推荐的方式:
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 client = HttpAsyncClientBuilder.create() .setConnectionManager(connManager) .setRedirectStrategy(new LaxRedirectStrategy()) .build(); client.start(); // 用同一个客户端执行多个请求 var res1 = client.execute(new HttpGet("https://www.google.com"), null); res1.get(); var res2 = client.execute(new HttpGet("https://www.google.com"), null); res2.get(); // 最后关闭客户端 client.close(); } }
方案2:标记连接管理器为共享模式
如果必须创建多个客户端实例,需要在构建后续客户端时调用setConnectionManagerShared(true),告诉客户端不要启动连接管理器的IOReactor(由第一个客户端负责启动):
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) .setConnectionManagerShared(true) .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(); // 注意:关闭顺序,最后关闭的客户端会关闭连接管理器,若需继续使用其他客户端,需手动管理连接管理器生命周期 client2.close(); client1.close(); } }
注意事项
- 使用共享连接管理器模式时,必须确保第一个客户端先启动,否则
IOReactor未运行会导致请求失败。 - 关闭客户端时,最后关闭的客户端会自动关闭连接管理器和
IOReactor,如果还有其他客户端需要使用,不要关闭连接管理器,或者手动调用connManager.shutdown()来终止资源。
内容的提问来源于stack exchange,提问作者derkoe
相关产品推荐
相关产品推荐

