【问题标题】:How can I ensure that my HttpClient 4.1 does not leak sockets?如何确保我的 HttpClient 4.1 不会泄漏套接字?
【发布时间】:2011-01-18 12:45:36
【问题描述】:

我的服务器根据每个请求使用来自内部 Web 服务的数据来构建其响应。我正在使用 Apache HttpClient 4.1 来发出请求。每个初始请求将导致对 Web 服务的大约 30 个请求。其中,4 - 8 个套接字最终会卡在 CLOSE_WAIT 中,永远不会被释放。最终这些卡住的套接字超过了我的 ulimit 并且我的进程用完了文件描述符。

我不想只提高我的 ulimit (1024),因为那只会掩盖问题。

我转移到 HttpClient 的原因是 java.net.HttpUrlConnection 的行为方式相同。

我已尝试根据请求移至 SingleClientConnManager,并在其上调用 client.getConnectionManager().shutdown(),但套接字仍然卡住。

我应该尝试解决这个问题,以便在没有正在运行的请求时得到 0 个打开的套接字,还是应该专注于请求持久性和池化?

为清楚起见,我将包含一些可能相关的细节:

操作系统:Ubuntu 10.10

JRE:1.6.0_22

语言:Scala 2.8

示例代码:

val cleaner = Executors.newScheduledThreadPool(1) 
private val client = {
    val ssl_ctx = SSLContext.getInstance("TLS")
    val managers = Array[TrustManager](TrustingTrustManager)
    ssl_ctx.init(null, managers, new java.security.SecureRandom())
    val sslSf = new org.apache.http.conn.ssl.SSLSocketFactory(ssl_ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
    val schemeRegistry = new SchemeRegistry()
    schemeRegistry.register(new Scheme("https", 443, sslSf))
    val connection = new ThreadSafeClientConnManager(schemeRegistry)
    object clean extends Runnable{ 
        override def run = {
            connection.closeExpiredConnections
            connection.closeIdleConnections(30, SECONDS)
        }
    }
    cleaner.scheduleAtFixedRate(clean,10,10,SECONDS)
    val httpClient = new DefaultHttpClient(connection)
    httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), new UsernamePasswordCredentials(username,password))
    httpClient
}
val get = new HttpGet(uri)
val entity = client.execute(get).getEntity
val stream = entity.getContent
val justForTheExample = IOUtils.toString(stream)
stream.close()

测试:netstat -a | grep {myInternalWebServiceName} | grep CLOSE_WAIT

(列出我的进程处于 CLOSE_WAIT 状态的套接字)

发表评论讨论:

此代码现在演示了正确的用法。

【问题讨论】:

    标签: java http sockets httpclient


    【解决方案1】:

    需要主动从连接池中驱逐过期/空闲连接,因为在阻塞 I/O 模型中,连接无法对 I/O 事件做出反应除非它们正在被读取/写入到。详情见

    http://hc.apache.org/httpcomponents-client-dev/tutorial/html/connmgmt.html#d4e631

    【讨论】:

    【解决方案2】:

    我已将 oleg 的答案标记为正确,因为它突出了关于 HttpClient 连接池的一个重要使用点。

    不过,要回答我最初的具体问题,即“我应该尝试解决 0 个未使用的套接字还是尝试最大化池化?”

    现在池解决方案已经到位并且工作正常,应用程序吞吐量增加了大约 150%。我将此归因于不必重新协商 SSL 和多次握手,而是根据 HTTP 1.1 重用持久连接。

    按照预期利用池绝对值得努力,而不是在每次请求等之后尝试通过调用 ThreadSafeClientConnManager.shutdown() 来解决问题。另一方面,如果您调用任意主机而不是像我一样重用路由,您可能会很容易发现有必要进行这种黑客攻击,因为 JVM 可能会对 CLOSE_WAIT 指定套接字的长寿命感到惊讶,如果你不经常收集垃圾。

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,并使用此处找到的建议解决了它:here。作者谈到了一些 TCP 基础知识:

      当一个 TCP 连接即将关闭时,它的最终确定是由双方协商的。可以将其视为以文明的方式违反合同。双方在文件上签字,一切都很好。在极客谈话中,这是通过 FIN/ACK 消息完成的。甲方发送一个 FIN 消息表明它想要关闭套接字。 B 方发送一个 ACK​​ 表示它已收到消息并正在考虑需求。然后 B 方进行清理并向 A 方发送 FIN。A 方以 ACK 响应,然后所有人走开。

      问题来了 当 B 没有发送它的 FIN 时。 A有点等待它。它有 启动了它的终结序列并且正在等待对方 做同样的事情。

      然后他提到RFC 2616, 14.10建议设置一个http头来解决这个问题:

      postMethod.addHeader("Connection", "close");
      

      老实说,我真的不知道设置此标头的含义。但它确实阻止了 CLOSE_WAIT 在我的单元测试中发生。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-04
        • 1970-01-01
        • 2011-07-12
        • 1970-01-01
        • 2016-11-28
        • 1970-01-01
        • 2015-08-26
        相关资源
        最近更新 更多