如何确保我的 HttpClient 4.1 不会泄漏套接字?
我的服务器使用来自内部 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 {myInternalWebServiceName} | grep {myInternalWebServiceName} grep CLOSE_WAIT
(列出我的进程处于 CLOSE_WAIT 状态的套接字)
发表评论讨论:
此代码现在演示了正确的用法。
My server uses data from an internal web service to construct its response, on a per request basis. I'm using Apache HttpClient 4.1 to make the requests. Each initial request will result in about 30 requests to the web service. Of these, 4 - 8 will end up with sockets stuck in CLOSE_WAIT, which never get released. Eventually these stuck sockets exceed my ulimit and my process runs out of file descriptors.
I don't want to just raise my ulimit (1024), because that will just mask the problem.
The reason I've moved to HttpClient is that java.net.HttpUrlConnection was behaving the same way.
I have tried moving to a SingleClientConnManager per request, and calling client.getConnectionManager().shutdown() on it, but sockets still end up stuck.
Should I be trying to solve this so that I end up with 0 open sockets while there are no running requests, or should I be concentrating on request persistence and pooling?
For clarity I'm including some details which may be relevant:
OS: Ubuntu 10.10
JRE: 1.6.0_22
Language: Scala 2.8
Sample code:
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()
Test: netstat -a | grep {myInternalWebServiceName} | grep CLOSE_WAIT
(Lists sockets for my process that are in CLOSE_WAIT state)
Post comment discussion:
This code now demonstrates correct usage.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
人们需要主动从连接池中逐出过期/空闲的连接,因为在阻塞 I/O 模型中,连接无法对 I/O 事件做出反应,除非正在读取/写入它们。有关详细信息,请参阅
http://hc.apache.org/ httpcomponents-client-dev/tutorial/html/connmgmt.html#d4e631
One needs to pro-actively evict expired / idle connections from the connection pool, as in the blocking I/O model connections cannot react to I/O events unless they are being read from / written to. For details see
http://hc.apache.org/httpcomponents-client-dev/tutorial/html/connmgmt.html#d4e631
我已将 oleg 的答案标记为正确,因为它突出显示了有关 HttpClient 连接池的重要使用点。
不过,要回答我最初的具体问题,即“我应该尝试解决 0 个未使用的套接字还是尝试最大化池化?”
现在池解决方案已就位并正常工作,应用程序吞吐量已增加了约 150%。我将此归因于不必重新协商 SSL 和多次握手,而是根据 HTTP 1.1 重用持久连接。
按预期利用池绝对值得努力,而不是尝试在每个请求后调用 ThreadSafeClientConnManager.shutdown() 等等。另一方面,如果您调用任意主机并且没有像我一样重用路由,您可能会很容易发现有必要进行此类黑客攻击,因为 JVM 可能会令您惊讶于 CLOSE_WAIT 指定套接字的长寿命,如果你不经常收集垃圾。
I've marked oleg's answer as correct, as it highlights an important usage point about HttpClient's connection pooling.
To answer my specific original question, though, which was "Should I be trying to solve for 0 unused sockets or trying to maximize pooling?"
Now that the pooling solution is in place and working correctly the application throughput has increased by about 150%. I attribute this to not having to renegotiate SSL and multiple handshakes, instead reusing persistent connections in accordance with HTTP 1.1.
It is definitely worth working to utilize pooling as intended, rather than trying to hack around with calling ThreadSafeClientConnManager.shutdown() after each request etcetera. If, on the other hand, you were calling arbitrary hosts and not reusing routes the way I am you might easily find that it becomes necessary to do that sort of hackery, as the JVM might surprise you with the long life of CLOSE_WAIT designated sockets if you're not garbage collecting very often.
我遇到了同样的问题,并使用此处找到的建议解决了它:此处。作者介绍了一些 TCP 基础知识:
然后他提到 RFC 2616, 14.10 建议设置 http 标头解决这个问题:
老实说,我真的不知道设置这个标头的含义。但它确实阻止了我的单元测试中发生 CLOSE_WAIT。
I had the same issue and solved it using the suggesting found here: here. The author touches on some TCP basics:
He then mentions RFC 2616, 14.10 to suggest setting up an http header to solve this issue:
Honestly, I don't really know the implications of setting this header. But it did stop CLOSE_WAIT from happening on my unit tests.