我的服务器使用来自内部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 | grep CLOSE_WAIT
(列出处于CLOSE_WAIT状态的进程的套接字)
发表评论讨论:
此代码现在演示正确的用法 .
3 回答
需要主动从连接池中驱逐过期/空闲连接,因为阻塞I / O模型连接不能对I / O事件做出反应,除非它们被读/写 . 详情见
http://hc.apache.org/httpcomponents-client-dev/tutorial/html/connmgmt.html#d4e631
我将oleg的答案标记为正确,因为它强调了关于HttpClient连接池的重要使用点 .
但是,要回答我特定的原始问题,“我应该尝试解决0个未使用的套接字还是尝试最大化池化?”
现在,池化解决方案已就位并正常工作,应用程序吞吐量增加了约150% . 我将此归因于不必重新协商SSL和多次握手,而是根据HTTP 1.1重用持久连接 .
绝对值得工作以按预期使用池,而不是试图在每个请求等之后调用ThreadSafeClientConnManager.shutdown() . 另一方面,如果你调用任意主机而不是按照我的方式重用路由,你可能很容易发现有必要做那种hackery,因为JVM可能会让你感到惊讶的是CLOSE_WAIT指定套接字的长寿命你不是经常收集垃圾 .
我有同样的问题,并使用此处的建议解决了它:here . 作者接触了一些TCP基础知识:
然后他提到RFC 2616, 14.10建议设置一个http标头来解决这个问题:
老实说,我真的不知道设置此 Headers 的含义 . 但它确实阻止了CLOSE_WAIT在我的单元测试中发生 .