【问题标题】:java httpclient connectionpool lease vs keep alivejava httpclient 连接池租约与保持活动状态
【发布时间】:2023-08-16 09:25:02
【问题描述】:

我在生产环境中使用 apache httpclient 4.5 有一段时间了,但最近,随着新用例的添加,系统开始出现故障。

我们有多个通过 REST Web 服务进行通信的服务,客户端是 apache httpclient 4.5 的包装器。

假设我有服务 A 与服务 B 通信。通信正常工作,直到我重新启动服务 B。由于超时,我从服务 A 发起的下一次调用服务 B 失败。在做了一些研究之后,我发现出于性能原因(不再有 2 次握手等),底层 TCP 连接被重用。由于服务器已重新启动,因此底层 TCP 连接已过时。

阅读文档后,我发现我可以在 n 秒后使我的连接失效。假设我重新启动服务 B,那么调用将在前 n 秒内失败,但之后会重建连接。这是我实现的keepAliveStrategy

    connManager = new PoolingHttpClientConnectionManager();
    connManager.setMaxTotal(100);
    connManager.setDefaultMaxPerRoute(10);
    ConnectionKeepAliveStrategy keepAliveStrategy = new DefaultConnectionKeepAliveStrategy() {

        public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
            long keepAliveDuration = super.getKeepAliveDuration(response, context);
            if (keepAliveDuration == -1) {
                keepAliveDuration = 45 * 1000; // 45 seconds
            }
            return keepAliveDuration;
        }
    };
    CloseableHttpClient closeableHttpClient = HttpClients.custom()
        .setConnectionManager(connManager)
        .setKeepAliveStrategy(keepAliveStrategy)
        .build();

我只是想知道这是否是该库的正确用法。我是这样做的,还是我让一切都变得过于复杂?

【问题讨论】:

    标签: java httpclient keep-alive


    【解决方案1】:

    不确定是否 100% 相同,但这是我的 2 美分:

    我们遇到了类似的问题(一段时间不活动后池中的连接断开)。当我们使用旧版本的 HttpClient (3.X) 时,我们使用了 http.connection.stalecheck 管理器参数,在使用服务器端关闭的连接时获得 IOException 的可能性受到了轻微的性能影响。

    升级到4.4+ 后,此方法已弃用并开始使用setValidateAfterInactivity,这是每次调用验证和运行时错误情况之间的中间地带:

    PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
    poolingConnManager.setValidateAfterInactivity(5000);
    

    void o.a.h.i.c.PoolingHttpClientConnectionManager.setValidateAfterInactivity(int ms)

    以毫秒为单位定义不活动期,在此之后必须重新验证持久连接才能租用给消费者。传递给此方法的非正值会禁用连接验证。此检查有助于检测在池中保持非活动状态时已过时(半关闭)的连接。

    如果您还控制所使用的 API,则可以根据您的客户端使用的时间调整保活策略。我们使用 AWS Cloudfront + ELB 对取消注册的实例进行连接耗尽,以确保在执行滚动升级时保持活动连接完全关闭。我想只要保证连接保持活动状态,比如 30 秒,传递给下面的连接管理器的任何值将始终确保有效性检查将减轻任何纯粹与过时/过期相关的运行时 I/O 错误连接。

    【讨论】:

    • 使用之前的有效性不会使我的连接过期,它只是继续尝试使用连接,即使在明确调用 connManager.closeExpiredConnections();
    • @jelle 和 Frederick 您能否指出在客户端保留 setValidateAfterInactivity() 与自定义 setKeepAliveStrategy() 之间的区别?不确定它们中的任何一个是否会覆盖或优先于另一个。