Apache HttpAsyncClient回调中二次请求阻塞问题求助
为什么Apache HttpAsyncClient回调中发起二次请求会阻塞?
你的代码出现阻塞的核心原因是回调线程被阻塞,导致I/O线程池无法处理新请求,具体细节:
- HttpAsyncClient的
completed、failed这类回调方法,默认是在客户端的I/O线程中执行的,不是独立的业务线程。 - 默认的HttpAsyncClient I/O线程池大小等于CPU核心数(比如单核心机器上只有1个I/O线程)。
- 当你在
completed里调用future1.get()时,会直接阻塞当前的I/O线程;而第二个请求的发送、响应处理都需要依赖I/O线程,此时唯一的I/O线程被卡住,无法处理新请求,形成死锁,代码自然一直停在get()这里。
解决方案1:用异步回调替代阻塞等待
既然是异步客户端,就延续异步的处理方式,不要在回调里同步等待结果,改用新的FutureCallback处理第二个请求:
package com.example.http; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.util.EntityUtils; public class AsyncClientHttpExample { public static void main(String[] args) { CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault(); httpClient.start(); final HttpGet request1 = new HttpGet("http://httpbin.org/ip"); httpClient.execute(request1, new FutureCallback<HttpResponse>() { @Override public void completed(HttpResponse result) { try { System.out.println(EntityUtils.toString(result.getEntity())); final HttpGet anotherRequest = new HttpGet("http://httpbin.org/headers"); // 改用异步回调处理第二个请求,不阻塞当前线程 httpClient.execute(anotherRequest, new FutureCallback<HttpResponse>() { @Override public void completed(HttpResponse anotherResult) { try { System.out.println("response 1 " + EntityUtils.toString(anotherResult.getEntity())); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void failed(Exception ex) { System.out.println("第二个请求失败:" + ex); } @Override public void cancelled() { System.out.println("第二个请求被取消"); } }); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void failed(Exception ex) { System.out.println(ex); } @Override public void cancelled() { } }); } }
解决方案2:配置独立的回调线程池
如果一定要在回调里做同步等待,可以给HttpAsyncClient配置专门的回调线程池,让回调在业务线程中执行,避免阻塞I/O线程:
package com.example.http; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.util.EntityUtils; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class AsyncClientHttpExample { public static void main(String[] args) { // 创建独立的回调线程池 ExecutorService callbackExecutor = Executors.newFixedThreadPool(4); CloseableHttpAsyncClient httpClient = HttpAsyncClientBuilder.create() .setDefaultCallbackExecutor(callbackExecutor) // 指定回调执行的线程池 .build(); httpClient.start(); final HttpGet request1 = new HttpGet("http://httpbin.org/ip"); httpClient.execute(request1, new FutureCallback<HttpResponse>() { @Override public void completed(HttpResponse result) { try { System.out.println(EntityUtils.toString(result.getEntity())); final HttpGet anotherRequest = new HttpGet("http://httpbin.org/headers"); Future<HttpResponse> future1 = httpClient.execute(anotherRequest, null); HttpResponse anotherResponse = future1.get(); // 现在阻塞的是回调线程池的线程,不会影响I/O线程 System.out.println("response 1 " + EntityUtils.toString(anotherResponse.getEntity())); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void failed(Exception ex) { System.out.println(ex); } @Override public void cancelled() { } }); } }
内容的提问来源于stack exchange,提问作者Jerry
相关产品推荐
相关产品推荐

