Java套接字保持活着很慢,重新打开套接字更快
我想要拿出一个简单的HTTP客户端的Java实现,它保持套接字打开并重用它来查询同一主机上的其他(或相同)URL 。Java套接字保持活着很慢,重新打开套接字更快
我有一个简单的实现,使用java.net.Socket,但不知何故,当我保持打开套接字的性能比当我不断创建一个新的更糟糕。
结果第一,低于全可执行代码:
成KeepAlive:
> java -server -Xms100M -Xmx100M -cp . KeepAlive 10 false
--- Warm up ---
188
34
39
33
33
33
33
33
34
33
Total exec time: 494
--- Run ---
33
35
33
34
44
34
33
34
32
34
Total exec time: 346
保持活动:在迭代#2
> java -server -Xms100M -Xmx100M -cp . KeepAlive 10 true
--- Warm up ---
18
61
60
60
78
62
59
60
59
60
Total exec time: 626
--- Run ---
26
59
60
61
60
59
60
60
62
58
Total exec time: 576
重塑插座每次提供更好的结果,从慢。 java(独立,无依赖)
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
public class KeepAlive {
private static final String NL = "\r\n";
private static final int READ_SIZE = 1000;
private Socket socket;
private DataOutputStream writer;
private BufferedReader reader;
public static void main(String[] args) throws Exception {
if (args.length == 2) {
KeepAlive ka = new KeepAlive();
System.out.println("--- Warm up ---");
ka.query(Integer.parseInt(args[0]), args[1].equals("true"));
System.out.println("--- Run ---");
ka.query(Integer.parseInt(args[0]), args[1].equals("true"));
} else {
System.out.println("Usage: keepAlive <n queries> <reuse socket>");
}
}
private void query(int n, boolean reuseConnection) throws Exception {
long t0 = System.currentTimeMillis();
if (reuseConnection) {
open();
for (int i = 0; i < n; i++) {
long tq0 = System.currentTimeMillis();
query();
System.out.println(System.currentTimeMillis() - tq0);
}
close();
} else {
for (int i = 0; i < n; i++) {
long tq0 = System.currentTimeMillis();
open();
query();
close();
System.out.println(System.currentTimeMillis() - tq0);
}
}
System.out.println("Total exec time: " + (System.currentTimeMillis() - t0));
}
private void open() throws Exception {
socket = new Socket();
socket.setKeepAlive(false);
socket.connect(new InetSocketAddress("example.org", 80));
writer = new DataOutputStream(socket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
private void query() throws Exception {
StringBuilder req = new StringBuilder();
req.append("GET/HTTP/1.1").append(NL);
req.append("Host: example.org").append(NL);
req.append("Connection: Keep-Alive").append(NL);
req.append(NL);
String reqStr = req.toString();
long t0 = System.currentTimeMillis();
writer.writeBytes(reqStr);
writer.flush();
String line;
int contentLength = 0;
while ((line = reader.readLine()) != null) {
if (line.startsWith("Content-Length: ")) {
contentLength = Integer.parseInt(line.substring(16));
}
if (line.equals("")) {
char[] buf = new char[contentLength];
int offset = 0;
while (offset < contentLength) {
int len = contentLength - offset;
if (len > READ_SIZE) {
len = READ_SIZE;
}
int ret = reader.read(buf, offset, len);
if (ret == -1) {
System.out.println("End of stream. Exiting");
System.exit(1);
}
offset += ret;
}
break;
}
}
}
private void close() throws Exception {
writer.close();
reader.close();
socket.close();
}
}
现在,我敢肯定,无论是:
Web服务器在处理快速的新要求很烂(HTTP Keep Alive and TCP keep alive)
东西是错误的我用的是缓冲读者,因为路这就是所有的时间都失去了,但看着其他可用的方法(和我尝试了一些),我无法找到我需要做什么来解决这个问题......我
任何想法,怎么可能做这个w ork更快?也许一个配置更改服务器本身?上...
解决方案
由于apangin下面解释由越慢PERF是Nagle算法,它是默认启用造成的。 使用setTcpNoDelay(真),我得到了更新以下perfs:
不保活:
java -server -Xms100M -Xmx100M -cp . KeepAlive 10 false
--- Warm up ---
49
22
25
23
23
22
23
23
28
28
Total exec time: 267
--- Run ---
31
23
23
24
25
22
23
25
33
23
Total exec time: 252
随着保活:
java -server -Xms100M -Xmx100M -cp . KeepAlive 10 true
--- Warm up ---
13
12
12
14
11
12
13
12
11
12
Total exec time: 168
--- Run ---
14
12
11
12
11
12
13
11
21
28
Total exec time: 158
所以在这里,我们可以看到保对于每次迭代,如果比较总执行时间,动态版本的执行效率会比非保持活动版本要好得多。 :)
这就是Nagle's algorithm的效果。 它延迟发送TCP数据包的预期更多传出数据。
Nagle的算法与TCP delayed acknowledgment严重交互写入读取方案。 这正是你的情况,因为writer.writeBytes(reqStr)
发送一个字节的字节的字节。
现在你有两个选择来解决该问题:
- 使用
socket.setTcpNoDelay(true)
禁用Nagle算法; - 在一个操作中发送完整的请求:
writer.write(reqStr.getBytes());
在这两种情况下,重复使用的连接将果然工作得更快。
reader.read(buf);
您的测试无效。您不一定阅读整个回复。您需要将其更改为对返回的数据进行计数的循环。如果你没有阅读整个回复,你会在保持活跃的情况下失去同步。
你没有错。这里测试的有效载荷非常小(约1Kb),这并不重要。我在上面的代码中添加了循环并更新了结果。和预期的一样。 – fabien
你并不是真的像这里一样测试。在一个你正在尽可能努力地锤击服务器。另一方面,你会在每个查询之间暂停一下。您是否尝试过每次运行测试客户端的总时间? – BevynQ
是的,这就是测试的重点:看看两种不同行为中哪一种表现最好。问题在于:“我能以多快的速度提出请求并获得响应,无论是否保持活力”。我为你添加了总执行时间。 – fabien