(见this question in ServerFault)
我有一个Java客户端,它使用Socket打开到同一台机器的并发连接。我正在目睹一种请求完成速度非常快的现象,但其他请求的延迟时间为100-3000毫秒。使用Wireshark进行数据包检查会在离开客户端之前显示所有SYN数据包超出第一个等待很长时间。我在Windows和Linux客户端都看到了这一点。可能是什么导致了这个?当客户端是Windows 2008或Linux机箱时会发生这种情况。
附加代码:
import java.util.*;
import java.net.*;
public class Tester {
public static void main(String[] args) throws Exception {
if (args.length < 3) {
usage();
return;
}
final int n = Integer.parseInt(args[0]);
final String ip = args[1];
final int port = Integer.parseInt(args[2]);
ExecutorService executor = Executors.newFixedThreadPool(n);
ArrayList<Callable<Long>> tasks = new ArrayList<Callable<Long>>();
for (int i = 0; i < n; ++i)
tasks.add(new Callable<Long>() {
public Long call() {
Date before = new Date();
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port));
}
catch (Throwable e) {
e.printStackTrace();
}
Date after = new Date();
return after.getTime() - before.getTime();
}
});
System.out.println("Invoking");
List<Future<Long>> results = executor.invokeAll(tasks);
System.out.println("Invoked");
for (Future<Long> future : results) {
System.out.println(future.get());
}
executor.shutdown();
}
private static void usage() {
System.out.println("Usage: prog <threads> <url/IP Port>");
System.out.println("Examples:");
System.out.println(" prog tcp 10 127.0.0.1 2000");
}
}
更新 - 如果在运行测试程序之前清除相关的ARP条目,则问题会一致地重现。我试过调整TCP retransmission timeout,但这没有帮助。此外,我们将此程序移植到.Net,但问题仍然存在。
更新2 - 3秒是从RFC 1122创建新连接的指定延迟。我仍然不完全理解为什么这里有重传,它应该由MAC层处理。另外,我们使用netcat重现了这个问题,因此它与java无关。
答案 0 :(得分:3)
看起来您使用单个底层HTTP连接。因此,在close()
的{{1}}上致电InputStream
之前,无法完成其他请求。即在处理回复之前。
或者您应该使用HTTP连接池。
答案 1 :(得分:3)
您在减少问题空间的大小方面做得很对。从表面上看,这是一个不可能的问题 - 在IP堆栈,语言和机器之间移动,但不是任意可重复的(例如,我不能在Windows和Linux上使用您的代码重新编写代码)。
一些建议,从堆栈顶部到底部:
代码 - 你说这发生在.Net和Java上。是否有任何语言/编译器组合不会发生?我使用您的客户端与sourceforge的SocketTest程序进行通信,并且“nc”使用相同的结果 - 没有延迟。同样,JDK 1.5和1.6对我没有任何影响。
- 假设您调整客户端发送请求的速度,比如每500毫秒一个。问题是否重现?
IP堆栈 - 可能会在出路时卡在堆栈中。我看到你已经排除了Nagle,但不要忘记像防火墙/ ip表这样的愚蠢的东西。我发现很难相信Win和Linux上的TCP堆栈已经被软化了,但你永远不知道。
- 环回接口处理可能很怪异。当你使用机器的真实IP时它会重现吗?整个网络(或者更好的是,使用x-over电缆连接到另一台机器)?
NIC - 如果数据包正在发送到卡上,请考虑卡的功能(TCP卸载或其他“特殊”处理)或NIC本身的怪癖。您是否与其他品牌的NIC获得了相同的结果?
答案 2 :(得分:3)
我从这次讨论中找不到真正的答案。我提出的最好的理论是:
我已尝试在Windows 7中调整超时但未成功。如果有人可以重现问题并提供解决方法,我将会非常有帮助。此外,如果有人知道为什么这种现象只发生在多个线程上的详细信息,那么听起来会很有趣。
我会尝试接受这个答案,因为我认为任何答案都没有提供真正的解释(见this discussion on meta)。
答案 3 :(得分:1)
如果其中一台机器是一个Windows框,我会看一下两者的最大并发连接数。请参阅:http://www.speedguide.net/read_articles.php?id=1497
我认为在某些情况下这是应用级限制,因此您必须按照指南提升它们。
此外,如果发生这种情况,您应该在违规机器上的系统事件日志中看到一些内容。
答案 4 :(得分:1)
使用HttpURLConnection打开到同一台计算机的并发连接的Java客户端。
同一台机器?客户接受什么应用程序?如果您自己编写该程序,也许您必须计算服务器接受客户端的速度。也许它只是一个糟糕(或不快速工作)的书面服务器应用程序。我认为服务器代码看起来像这样;
ServerSocket ss = ...;
while (acceptingMoreClients)
{
Socket s = ss.accept();
// On this moment the client is connected to the server, so start timing.
long start = System.currentTimeMillis();
ClientHandler handler = new ClientHandler(s);
handler.start();
// After "handler.start();" the handler thread is started,
// So the next two commands will be very fast done.
// That means the server is ready to accept a new client.
// Stop timing.
long stop = System.currentTimeMillis();
System.out.println("Client accepted in " + (stop - start) + " millis");
}
如果这个结果不好,那么你知道问题出在哪里 我希望这可以帮助您更接近解决方案。
要进行测试,您是否使用从DHCP服务器或127.0.0.1
收到的IP
如果来自DHCP服务器,那么一切都通过您公司的路由器/交换机/ ....这可能会减慢整个过程。
否则:
Linux:Wireshark只能看到硬件层的流量。 Linux不会在软件层上重定向。这也是InetAddress.getLocalhost().getAddress()
127.0.0.1
返回的原因。
因此,当您使用Windows时,使用Wireshark看不到SYN数据包是很正常的。
马亭。
答案 5 :(得分:1)
由于问题无法重现,除非您清除关联的ARP缓存,从发出ARP请求到发出3秒延迟之后,整个数据包跟踪从定时角度看是什么样的?
如果打开与两个不同IP的连接会发生什么?两者的第一次连接是否会成功?如果是这样,那应该排除任何JVM或库问题。
在ARP响应到来之前,无法发送第一个SYN。可能OS或TCP堆栈使用超时而不是事件超过第一个尝试在关联的MAC地址未知时打开连接的线程。
想象一下以下场景:
答案 6 :(得分:1)
您在多个客户端上看到这一点,具有不同的操作系统,以及不同的应用程序环境(我假设)相同的操作系统,这强烈表明它与网络或服务器有关,而不是客户端。您的意见强调了清除ARP表再现问题。
您是否在交换机上有两台具有相同MAC地址的计算机? (其中一个可能是一个欺骗MAC地址的路由器。)
或者更有可能的是,如果我正确地回想起ARP,两台机器具有相同的硬编码IP地址。当客户端发出“谁是IP 123.456.123.456”时,两人都会回答,但实际上只有一人会在听。
另一种可能性(我在公司环境中看到过这种情况)是一个流氓DHCP服务器,再次向两台机器提供相同的IP地址。
答案 7 :(得分:0)
当我获得DNS超时时,我看到过类似的行为。要对此进行测试,您可以直接使用IP地址,也可以在hosts文件中输入IP地址。
答案 8 :(得分:0)
设置socket.setTcpNoDelay( true )
有帮助吗?
答案 9 :(得分:0)
您是否曾尝试使用strace运行客户端来查看系统调用。
过去,在调试一些神秘的网络问题时,对我非常有帮助。
答案 10 :(得分:0)
服务器上的监听积压是什么?它接受连接的速度有多快?如果待办事项已填满,则操作系统会忽略连接尝试。 3秒后,客户端再次尝试,并且现在已经清除积压。