【问题标题】:Apache HttpClient and HttpConnection in a multithreaded applicatio多线程应用程序中的 Apache HttpClient 和 HttpConnection
【发布时间】:2016-06-26 15:52:58
【问题描述】:

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

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

我目前对每个请求都使用一个 PoolingHttpClientConnectionManagerHttpClientBuilder.setConnectionManager(connectionManager).build()

我有一种真正浪费资源的感觉,而且我还看到每台服务器的许多连接处于ESTABLISHED 状态,尽管我使用的是池连接管理器。 (每台服务器的请求都是一一发送的,不并发)

【问题讨论】:

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


    【解决方案1】:

    我目前对每个请求都使用一个 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请求。每个请求都在客户端的上下文中执行(它的配置 - 即代理、并发、keep-alive 等)对请求的每个响应都必须关闭(reset()、close(),不记得确切的名称) 以释放连接,以便将其重用于另一个请求。

    【讨论】:

    • 谢谢。您能否详细说明 HttpConnection 和 HttpClient 之间的关系?什么会被“浪费”? HttpClient 是重物吗?在应用程序的整个生命周期中将其保留为 bean 是否有意义(从而避免需要 close() 它?
    【解决方案2】:

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

    【讨论】:

    • 是的,premature optimization is the root of all evil. 说 Knuth。但这不是优化。我需要对我正在使用的重要框架进行最佳实践和更深入的了解
    【解决方案3】:

    我同意@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 连接就完全足够了。但是,如果您想更快地做一些事情(例如并行上传两个图像),那么您可以受益于针对同一服务器打开多个连接。 (当然,这意味着您的应用程序实际上是多线程的。)

    结论

    如果它不是对时间要求严格的应用程序,最简单的方法是不要将任何东西汇集到服务器上,当你有数据要发送时。您可以开始对此进行过度优化并争取 10 毫秒的改进,但代价是严重的意外复杂性。

    【讨论】:

    • 实际上我让 poolinghttpclientconnectionmanager 决定是否保持连接打开。我主要对与httpclient的关系感兴趣
    • 我想知道它如何确定何时值得保持连接或断开连接。它不知道您的负载配置文件,因此它可能会碰巧断开某些连接——即使它需要 2 秒后,或者不断开连接——即使在接下来的半小时内没有任何通信发生。我猜你要么按照我们的建议完全忽略这一点,要么如果你想完全优化它,就必须更深入地研究细节,不是吗?
    • 有最佳实践。这有点太旧了,但这就是我正在寻找的答案。 stackoverflow.com/q/1281219/1517569
    【解决方案4】:

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

    Connection: keep-alive
    

    但是,这只是一个请求,您要连接的服务器无论如何都可能断开连接。

    HTTP 1.1 默认提供此功能,但默认超时非常短。也许那里有一些可能的配置。在任何情况下,如果您收到标题中包含Connection: close 的响应,则必须关闭连接。

    更多详情,请咨询rfc2616,尤其是第8节“持久连接”

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

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

    【讨论】:

    • 谢谢。但是如果我在 netstat 上看到ESTABLISHED 状态的连接,这意味着很可能 http 会话仍然存在,不是吗?
    • 顺便说一句,您的链接指向HttpClient 3.1,这与我使用的 4.5 相比有很大差异
    • 并非如此。您正在查看客户对连接状态的看法。这通常是正确的,但也可能是错误的。基本上,真正了解已建立的连接是否真的可用的唯一方法是尝试使用它。如果不可用,它会失败,即使成功,它也是连接的“最后已知状态”,而不是合约。初始连接(取决于具体情况)有时会更糟,优化为“建立连接”而不与服务器通信,直到实际传输第一个字节。
    • @Nati 我的文档链接可能很旧,但其中大部分内容都在协议标准中,甚至比我链接到的文档还要旧(嗯,除了 rfc)。当您看到“已建立”时,不能相信连接是“真正打开的”。您必须向它传输数据(并读取响应)才能使其工作。最终超时将使一切与现实保持一致,但数据在您发现它不可用之前看起来不错一段时间。
    猜你喜欢
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 2014-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-13
    相关资源
    最近更新 更多