【发布时间】:2014-11-15 20:49:11
【问题描述】:
出于测试/基准测试的目的,我想编写一个循环执行以下任务的 Java 程序:
- 通过 HTTP GET 从服务器加载数据
- (根据收到的数据生成答案 - 此时并不重要)
- 通过 HTTP POST 将答案发送到同一服务器
这个循环同时在多个线程上运行。
启动后,程序可以在短时间内正常运行,并且每线程每秒能够执行约 300 个循环(网络服务器在同一台机器上运行)。但 5-7 秒后,我收到了BindException: Address already in use。
在 20-30 秒的冷却时间后重新启动程序会导致相同的行为;当我立即重新启动它而不等待时,它会立即崩溃......所以我想这可能是绑定资源的问题。
使用HttpURLConnection 是一种快速而肮脏的方法。相关部分:
从网络服务器获取数据
public String fetchData() throws IOException {
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setUseCaches(false);
conn.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
conn.disconnect();
return response.toString();
}
发送答案
public void sendData(byte[] data) throws IOException {
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(data);
os.close();
conn.disconnect();
}
在线程内调用这两个方法
@Override
public void run() {
while(true) {
try {
String data = fetchData();
String answer = // ... generating answer
sendData(answer.getBytes("UTF-8"));
} catch (IOException e) {
// ...
}
}
}
线程之间没有共享一个 URL 对象 - 每个线程都有自己的 URL 实例(但是,每个实例都指向同一个地址)。
编辑:
这是发生的第一个异常的堆栈跟踪:
java.net.BindException: Address already in use: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at sun.net.NetworkClient.doConnect(Unknown Source)
at sun.net.www.http.HttpClient.openServer(Unknown Source)
at sun.net.www.http.HttpClient.openServer(Unknown Source)
at sun.net.www.http.HttpClient.<init>(Unknown Source)
at sun.net.www.http.HttpClient.New(Unknown Source)
at sun.net.www.http.HttpClient.New(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at PayloadProcessor.fetchData(PayloadProcessor.java:66)
at PayloadProcessor.run(PayloadProcessor.java:32)
at java.lang.Thread.run(Unknown Source)
它出现在 fetchdata 方法的下面一行:
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
我删除了对 disconnect() 的调用(正如 Aaron 指出的那样) - 不幸的是,同样的问题仍然存在。
让我们假设一切都正确关闭(不确定是否是这种情况,只是假设它) - 可能是程序太快了吗?我在 stackoverflow 上找到了另一个 post,“解决方案”是添加一个简单的 Thread.sleep - 但由于它是一个基准测试并且应该尽可能快地运行,我不喜欢这样。负载测试工具如何处理这个问题?
我将线程数减少到1,即使这样问题仍然存在。
【问题讨论】:
-
在这里发布错误的完整堆栈跟踪可能很有用。无论如何,我向您推荐的第一件事是将 disconnect 调用包含在 finally 子句中:可能出现了太多异常,以至于连接没有被正确关闭,导致泄漏。
-
谢谢,我添加了堆栈跟踪。你是对的,确实有很多异常 - 但是出于调试目的,我现在在第一个异常引发后终止程序(上面的代码中没有显示)并且仍然有同样的问题。第一个异常已经是 BindException。
-
请在您的操作系统中添加命令
netstat -anpt | grep <ip_of_server_your_connected>或其模拟的结果。 -
您与一台服务器有多个连接。 Web 服务器(或 servlet 容器)有连接限制。你用什么服务器?
标签: java multithreading sockets http networking