【问题标题】:HttpClient can't get response from serverHttpClient 无法从服务器获得响应
【发布时间】:2014-01-02 09:58:29
【问题描述】:

这个问题让我们整个团队卡了半天!

我们使用 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 是否存在性能问题?

谢谢大家~

【问题讨论】:

    标签: java sockets httpclient


    【解决方案1】:

    我试图重现阻塞的 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)一个连接池使用最多2个连接到1个服务器。例如。即使 5 个线程同时对 1 个服务器执行各种请求,连接池也只打开 2 个连接,将它们重新用于所有请求,并确保 1 个连接在任何给定时间被 1 个线程使用。这可以对客户端性能产生非常积极的影响,并显着减少服务器上的负载。唯一必须确保的是在 finally 块中调用response.close()(这可以确保连接返回到连接池)。通过使用NoConnectionReuseStrategy,您基本上禁用了连接池:对于每个请求,都会创建一个新连接。我建议您为类别org.apache.http.impl.conn.PoolingHttpClientConnectionManager 启用调试日志记录,它非常有用。

    关于httpPut.releaseConnection() 的注释:这实际上并没有释放连接,它只是确保您可以在下一个请求中重新使用“httpPut”对象(请参阅apidocs,点击显示的链接)。另请注意,在您的“httpGet”代码中,您在“httpPut”而不是“httpGet”上调用releaseConnection()。

    【讨论】:

      【解决方案2】:

      不久前遇到了这个问题。如果其他人遇到这个问题,这篇文章可能会有用。

      我正在使用 Java Servlet 来处理我的请求。当我使用 PrintWriter 实例写入响应流时,我的客户端被阻止了。尝试直接写入OutputStreamresponse.getOutputStream.write("myresponse"),它成功了。

      【讨论】:

        猜你喜欢
        • 2020-06-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-01
        • 2017-06-25
        • 2015-11-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多