多线程应用程序中的Apache HttpClient和HttpConnection

时间:2016-03-11 11:52:50

标签: java multithreading apache-httpclient-4.x apache-httpcomponents

在我的mutlithreaded应用程序中,我发送一些http请求到一些http服务器,我会说10台服务器,每台服务器300个不同的请求,大约每小时一次,没什么太严重。

我的问题是:我应该为所有传出连接保留一个HttpClient吗?也许每个唯一目标服务器一个?或者每次“迭代”一次(每小时开始大约需要10分钟)?

我目前正在为每个请求使用一个PoolingHttpClientConnectionManagerHttpClientBuilder.setConnectionManager(connectionManager).build()

我有一种真正浪费资源的感觉,我也看到每个服务器ESTABLISHED状态有很多连接,尽管我正在使用池连接管理器。 (每个服务器的请求是逐个发送的,并不是并发的)

4 个答案:

答案 0 :(得分:2)

  

我目前正在为每个请求使用单个PoolingHttpClientConnectionManager和HttpClientBuilder.setConnectionManager(connectionManager).build()。

为每个请求构建一个新的HttpClient是一个巨大的浪费。您应该为每个配置使用HttpClient(每个客户端可以具有不同的连接管理器,最大并发请求等)或应用程序的每个独立模块(以便不在其他独立模块之间创建依赖关系)。

另请注意,.build()会返回CloseableHttpClient,这意味着您在使用完毕后应该调用httpClient.close(),否则可能会泄漏资源。

响应来自@Nati的评论更新:

  

会浪费什么"浪费" ? HttpClient是一个重物?

Here you can see the source code for the creation of an http client.正如您所看到的那样,代码很多,并且对每个请求执行都没有意义。这种不必要的消耗CPU并产生大量垃圾,这降低了整个应用程序的性能。你做的分配越少越好!换句话说,为每个请求创建新客户端有没有好处 - 只有缺点。

  

在将应用程序的整个生命周期中保持为bean的任何意义

恕我直言,除非它非常(非常)很少使用。

  

HttpConnection和HttpClient之间的关系

每个http客户端都可以执行多个http请求。每个请求都在客户端的上下文中执行(它的配置 - 即代理,并发,保持活动等)每个对请求的响应都必须关闭(reset(),close(),don&#39 ; t记住确切的名称)以释放连接,以便它可以被重用于另一个请求。

答案 1 :(得分:1)

我会说如果没有破坏就不要修理它。我的意思是只要最简单的配置满足您的需求就可以使用它,并且不会引入任何复杂性只是为了满足未来的可扩展性需求。额外部分意味着额外的复杂性,这意味着更多的错误。一旦您看到当前配置不再保持增加的负载,就可以进行估算并添加资源。我希望这有帮助

答案 2 :(得分:1)

我同意@Michael Gantman没有解决它。

我会说修复或不修复取决于您的负载配置文件。

保持与否保持联系?

例如,如果您一次向10台服务器发送300个请求,之后您在一小时内没有执行任何操作,那么 resource-wise 保留任何TCP都没有意义/ IP连接打开(因为使用HTTP / 1.1)整个小时。

但是,如果您每5秒钟与服务器通信一次,则可以考虑保持连接处于打开状态。此外,如果要通过反复消除连接建立来最小化延迟,可以考虑保持连接打开。

为此,您必须使用HTTP / 1.1。你可以找到很多例子,例如DefaultHttpClient keep alive connection on multiple requests

要保留多少个连接?

同样,取决于您的负载配置文件。你说你有10台服务器。如果您以串行方式发送一台服务器的数据,那么每台服务器使用http / 1.1建立一个http连接就足够了。但是,如果您想要更快速地执行某些操作(例如,并行上传两个图像),那么您可以在同一服务器上打开多个连接。 (当然这意味着您的应用程序是真正的多线程。)

<强>结论

如果它不是一个时间关键型应用程序,最简单的方法就是在有数据发送时不要将任何内容集中到服务器上。你可以开始过度优化它,并以严重的意外复杂性为代价争取10ms的改进。

答案 3 :(得分:0)

在客户端请求的HTTP 1.0标头中,您需要

                List<PostItem> Guides = response.body().getGuides();

            for(int i = 0 ; i < Guides.size() ; i ++ ) {
                for (int b = 0; b < Guides.get(i).English.size() ; b++){
                    Log.LogInfo("English Result Is: " + Guides.get(i).English.get(i).getEnglishHTC());
                    Log.LogInfo("English Result Is: " + Guides.get(i).English.get(i).getEnglishNexus());
                    Log.LogInfo("English Result Is: " + Guides.get(i).English.get(i).getEnglishSamsung());
                    Log.LogInfo("English Result Is: " + Guides.get(i).English.get(i).getEnglishSony());
                }
                for (int b = 0; b < Guides.get(i).Arabic.size() ; b++){
                    Log.LogInfo("Arabic Result Is: " + Guides.get(i).Arabic.get(i).getArabicHTC());
                    Log.LogInfo("Arabic Result Is: " + Guides.get(i).Arabic.get(i).getArabicNexus());
                    Log.LogInfo("Arabic Result Is: " + Guides.get(i).Arabic.get(i).getArabicSamsung());
                    Log.LogInfo("Arabic Result Is: " + Guides.get(i).Arabic.get(i).getArabicSony());
                }
            }

但是,这只是一个请求,而您连接的服务器可能会丢弃连接。

HTTP 1.1默认提供此功能,但默认超时非常短。也许那里有一些可能的配置。在任何情况下,如果您在其标题中收到`Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 3 column 18 path $.Guides 的回复,则必须关闭该连接。

有关详细信息,请参阅rfc2616,尤其是第8节“持久连接”

因此,似乎正确的做法是确保HTTP 1.1处理(默认情况下连接保持打开1.1)并且不对HttpClient做任何“特殊”操作。 According to the second and third sections of the HttpClient homepage,默认情况下,客户端将尽可能长时间地保持持久连接。

我的建议是(如果在客户端连接端添加例程/线程/控制器)具有在同一范围内排序的特定服务器/端口的所有相关连接(全部在同一线程上或按相同顺序排序) ),这可能会降低您遇到连接关闭逻辑的可能性;但是,你不能真正强迫连接保持开放(原因很明显)。