如何保持多个 Java HttpConnections 对同一目的地开放
我们经常使用 HttpURLConnection API 向同一提供者调用 REST API(一种聚合用例)。我们希望保持 5 个连接池始终对提供商主机开放(始终相同的 IP)。
正确的解决方案是什么?这是我们尝试过的:
System.setProperty("http.maxConnections", 5); // set globally only once
...
// everytime we need a connection, we use the following
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setUseCaches(true);
...
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
...
此时我们读取输入流,直到 BufferedReader 不再返回字节。如果我们想重用与提供者的底层连接,那么在那之后我们该怎么办?我们的印象是,如果输入流被完全读取,连接就会被添加回池中。
它已经以这种方式工作了几个星期,但今天它停止工作并产生此异常: java.net.SocketException: Too much open files
我们发现许多套接字处于 CLOSE_WAIT 状态,如下所示(通过运行 >lsof
): java 1814 root 97u IPv6 844702 TCP colinux:58517->123.123.254.205:www (CLOSE_WAIT)
conn.getInputStream().close() 或 conn.disconnect() 都不会完全关闭连接并将其从池中删除?
We are using HttpURLConnection API to invoke a REST API to the same provider often (kind of an aggregation usecase). We want to keep a pool of 5 connections always open to the provider host (always the same IP).
What is the proper solution? Here is what we tried:
System.setProperty("http.maxConnections", 5); // set globally only once
...
// everytime we need a connection, we use the following
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setUseCaches(true);
...
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
...
At this point we read the input stream until the BufferedReader returns no more bytes. What do we do after that point if we want to reuse the underlying connection to the provider? We were under the impression that if the input stream is completely read, the connection is then added back to the pool.
It's been working for several weeks this way, but today it stopped working producing this exception: java.net.SocketException: Too many open files
We found numerous sockets in the CLOSE_WAIT state like this (by running lsof
):java 1814 root 97u IPv6 844702 TCP colinux:58517->123.123.254.205:www (CLOSE_WAIT)
Won't either conn.getInputStream().close() or conn.disconnect() completely close the connection and remove it from the pool?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我们在 Java 5 上也遇到了这个问题,我们的解决方案是切换到带有池连接管理器的 Apache HttpClient。
Sun 的 HTTP URL 处理程序的 keepalive 实现有很多错误。没有维护线程来关闭空闲连接。
keepalive 的另一个更大的问题是你需要删除响应。否则,该连接也将被孤立。大多数人没有正确处理错误流。请参阅我对此问题的回答,了解有关如何正确读取错误响应的示例,
HttpURLConnection.getResponseCode() 在第二次调用时返回 -1
We had this problem also on Java 5 and our solution is to switch to Apache HttpClient with pooled connection manager.
The keepalive implementation of Sun's URL handler for HTTP is very buggy. There is no maintenance thread to close idle connections.
Another bigger problem with keepalive is that you need to delete responses. Otherwise, the connection will be orphaned also. Most people don't handle error stream correctly. Please see my answer to this question for an example on how to read error responses correctly,
HttpURLConnection.getResponseCode() returns -1 on second invocation
来自此处:
我读到这篇文章就好像您的解决方案应该有效,但您也可以自由调用 close 并且连接仍将被重用。
From here:
I read this as if your solution should work, but that you are also free to call close and the connection will still be reused.
disown 引用的 参考 是真正有帮助的是什么。
我们知道 Apache HttpClient 更好,但这需要另一个 jar,并且我们可能会在小程序中使用此代码。
调用 HttpURLConnection.connect() 是不必要的。我不确定它是否会阻止连接重用,但我们将其删除了。关闭流是安全的,但在连接上调用
disconnect()
将阻止重用。此外,设置sun.net.http.errorstream.enableBuffering=true
也有帮助。这是我们最终使用的:
The reference cited by disown was what really helped.
We know Apache HttpClient is better, but that would require another jar and we might use this code in an applet.
Calling
HttpURLConnection.connect()
was unnecessary. I'm not sure if it prevents connection reuse, but we took it out. It is safe to close the stream, but callingdisconnect()
on the connection will prevent reuse. Also, settingsun.net.http.errorstream.enableBuffering=true
helps.Here is what we ended up using: