Java 如何确保我的HttpClient 4.1不会泄漏套接字?
我的服务器使用来自内部web服务的数据,根据每个请求构造其响应。我正在使用ApacheHttpClient 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 示例代码:Java 如何确保我的HttpClient 4.1不会泄漏套接字?,java,http,sockets,httpclient,Java,Http,Sockets,Httpclient,我的服务器使用来自内部web服务的数据,根据每个请求构造其响应。我正在使用ApacheHttpClient 4.1发出请求。每个初始请求将导致对web服务的大约30个请求。其中,4-8个插座将被卡在CLOSE_WAIT中,永远不会释放。最终,这些被卡住的套接字超过了我的ulimit,我的进程耗尽了文件描述符 我不想仅仅提高我的ulimit(1024),因为这只会掩盖问题 我转到HttpClient的原因是java.net.HttpUrlConnection的行为方式与此相同 我已经尝试过为每个请
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|u WAIT
(列出处于关闭等待状态的进程套接字)
评论后讨论:
此代码现在演示了正确的用法。需要主动从连接池中清除过期/空闲的连接,因为在阻塞I/O模型中,除非从中读取/写入I/O事件,否则连接无法对I/O事件作出反应。详情请参阅
我已将oleg的回答标记为正确,因为它突出了HttpClient连接池的一个重要使用点 不过,为了回答我最初的特定问题,我应该尝试解决0个未使用的套接字,还是尝试最大化池 现在池解决方案已经就位并正常工作,应用程序吞吐量增加了约150%。我认为这是因为不必重新协商SSL和多次握手,而是根据HTTP 1.1重用持久连接
按照预期使用池是绝对值得的,而不是尝试在每次请求后调用ThreadSafeClientConnManager.shutdown()等等。另一方面,如果你调用任意主机,而不是像我这样重用路由,你可能会很容易发现有必要进行这种黑客行为,如果您不经常进行垃圾收集,JVM可能会让您惊讶于指定套接字的长使用寿命。我遇到了同样的问题,并使用此处的建议解决了它:。作者介绍了一些TCP基础知识: 当TCP连接即将关闭时,其最终确定由双方协商。把它看作是以文明的方式违反合同。双方都签了字,一切都好。在极客对话中,这是通过FIN/ACK消息完成的。甲方发送一条FIN消息,表明其想要关闭套接字。乙方发送ACK,表示其已收到该消息并正在考虑该需求。然后乙方进行清理并向甲方发送FIN。甲方回复ACK,所有人都走开 问题来了 当B不发送它的鳍时。A有点等不及了。它有 已启动其最终确定序列,正在等待另一方 做同样的事 然后,他建议设置一个http头来解决这个问题:
postMethod.addHeader("Connection", "close");
老实说,我真的不知道设置此标题的含义。但是它确实阻止了我的单元测试发生CLOSE_WAIT。谢谢你的回答,我同意文档中建议这应该是一种有效的措施。但是,随着脱离线程清理器的实现,孤立到CLOSE_WAIT中的套接字数量仍在可靠地增长,因此它并没有发挥作用。我已经在我的问题中添加了一些实现细节。我放弃了。我没有意识到,在实际的应用程序代码中,我正在发出补充图像请求以执行业务逻辑。他们仍然在使用HttpClient引入之前的WS.url方法,并且留下了套接字。断开的链接。(请包含标题和说明以及链接。)也许这是相同的信息?如果其他人偶然发现了这一点:相关API在HttpClient 4.2和4.3之间发生了变化: