HttpClient无法从服务器获取响应

3
这个问题让我们整个团队都被卡住了半天!
我们使用Apache HttpClient 4.3.x从一个提供HTTP API的存储服务器上获取和提交数据。为了提高性能,我们使用了PoolingHttpClientConnectionManager:
public HttpClient createHttpClient() {
    Registry registry = RegistryBuilder.create()....build();
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
    connectionManager.setMaxTotal(50);
    connectionManager.setDefaultMaxPerRoute(50);

    CloseableHttpClient httpClient = HttpClients.custom()
       .setConnectionManager(connectionManager)
       .build();
    return httpClient;
}

接下来我们在程序中使用httpClient实例,每次http请求都重复使用它:

全局httpClient:

HttpClient httpClient = createHttpClient();

提交一些数据:

HttpPost httpPut = new HttpPost("...");
HttpResponse response = httpClient.execute(httpPut);

// Notice we get the response content here!
String content = EntityUtils.toString(response.getEntity());
System.out.println(content);

httpPut.releaseConnection();
response.close();

然后获得:
HttpGet httpGet = new HttpGet("...");

// Blocked at this line !!!!
HttpResponse response = httpClient.execute(httpGet);

String content = EntityUtils.toString(response.getEntity());
System.out.println(content);

httpPut.releaseConnection();
response.close();    

请注意这一行:// Blocked at this line !!!! 程序在那一行被阻塞,无法执行下一行。在调试模式下,我可以看到它被阻塞在:
SocketInputStream.socketRead0()

我搜索了很多问题和文档,但都没有获得成功。


我的同事只需设置NoConnectionReuseStrategy.INSTANCE就解决了问题:

 HttpClients.custom()
       .setConnectionManager(connectionManager)
       // Following line fixed the problem, but why?
       .setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE)
       .build();

现在它没有被阻止,但为什么呢?

“重用连接”是什么意思?使用NoConnectionReuseStrategy是否存在性能问题?

谢谢大家~

2个回答

4
我试图重现阻塞的 http-get(也是为了自己做练习),但即使没有关闭响应,我也无法让它阻塞。唯一一次成功阻塞http-get的情况是通过对 response.getEntity().getContent() 的调用,未从返回的 InputStream 中读取数据并且未关闭 InputStream。 我的测试使用Tomcat 7.0.47作为服务器,有两个非常简单的servlet(一个响应get请求返回 "OK",另一个回显post请求)。客户端启动50个线程,每个线程执行30个交替的http-get和http-post请求(共1500个请求)。客户端没有使用 RegistryBuilder,而是使用默认的(由 PoolingHttpClientConnectionManager 创建的)连接管理器。
关于NoConnectionReuseStrategy:默认情况下(使用HttpClients.createDefault()创建的HttpClient,我使用了org.apache.httpcomponents:httpclient:4.3.1),将使用连接池,最多连接到1个服务器的2个连接。例如,即使有5个线程在同时向1个服务器发送各种请求,连接池也仅打开2个连接,重用它们进行所有请求,并确保每次只有1个线程使用1个连接。这可以对客户端性能产生非常积极的影响,并显着减少服务器的负载。唯一必须确保的是在finally块中调用response.close()(这确保连接被返回到连接池)。通过使用NoConnectionReuseStrategy,您基本上禁用了连接池:为每个请求创建一个新连接。我建议您为类别org.apache.http.impl.conn.PoolingHttpClientConnectionManager启用调试日志记录,这非常有帮助。
关于 httpPut.releaseConnection() 的说明:这并不会真正释放连接,它只确保您可以在下一次请求中重用“httpPut”对象(请参见 apidocs,按照显示的链接进行操作)。还要注意,在“httpGet”的代码中,您调用了“httpPut”而不是“httpGet”的releaseConnection()方法。

0
刚刚遇到了这个问题。如果其他人也遇到了这个问题,这篇文章可能会有所帮助。
我正在使用Java Servlet来处理请求。当我使用PrintWriter实例向响应流写入时,我的客户端被阻塞了。尝试直接向OutputStream写入response.getOutputStream.write("myresponse"),它可以正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接