Java HttpServer使用keepalive时速度非常慢

Java HttpServer使用keepalive时速度非常慢,java,keep-alive,com.sun.net.httpserver,Java,Keep Alive,Com.sun.net.httpserver,下面的HttpServer程序在没有HTTP keepalive的情况下可以轻松处理8000个请求/秒,但在keepalive的情况下只能处理22个请求/秒 import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.Http

下面的HttpServer程序在没有HTTP keepalive的情况下可以轻松处理8000个请求/秒,但在keepalive的情况下只能处理22个请求/秒

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class HSTest {
    public static void main(String[] args) throws IOException {
        HttpServer hs = HttpServer.create(new InetSocketAddress(30006), 1000);
        hs.createContext("/", new HttpHandler() {
            public void handle(HttpExchange he) throws IOException {
                byte[] FILE = "xxxxx".getBytes();       
                he.sendResponseHeaders(200, FILE.length);           
                OutputStream os = he.getResponseBody();
                os.write(FILE);
                os.flush();
                he.close();
            }
        });
        hs.start();
    }
}
以下是keepalive的外观:

注意数据包6、12和17的巨大延迟。第一次之后,它们总是略高于40毫秒。相反,如果没有keepalive,一切都很好:

在第一次ms结束之前,总共有3个请求

我在debian sid Linux amd64上使用OpenJDK 8,客户机和服务器都在同一台机器上,并通过本地主机接口进行通信。为了测试,我使用ab-N100000-C1http://localhost:30006/ 无保留和ab-n 100000-c 1-khttp://localhost:30006/ 默认情况下,keepalive以及curl和chromium都使用keepalive


那么,是什么导致HTTP keepalive请求延迟40毫秒,以及如何使我的服务器快速运行呢?

正如评论中所暗示的,我认为主要的问题在于,在不改变默认设置的情况下,要求单次连接上的HTTP吞吐量达到极高是不正常的。如果您在允许多个客户机时得到类似的灾难性数字,例如-c100标志到ab,那将是另一个问题。KeepAlive总体上具有占用每个连接服务器上一个线程的效果

我认为你所观察到的与TCP_NODELAY-Nagle的算法有关,可能伴随着延迟确认。就数据包的数量而言,no-keepalive案例足够短,您永远不会被它击中

特别提到Linux上最多40毫秒的延迟 提到一个Java属性,用于在基本Java HTTP服务器中启用TCP_节点延迟。我很有信心,如果启用此标志,您将看到不同的行为


另一种方法是将延迟确认超时更改为不同于40毫秒的时间。例如,请参见

我认为您应该告诉我们更多有关用于测试此功能的客户端设置的信息。在KeepAlive场景中,似乎所有请求都有一个源端口。在非KeepAlive中,不仅每个请求都有新的连接,而且连接尝试是交错的。您使用什么样的测试环境?例如,KeepAlive使用不当会导致100个固定线程处于忙碌状态,这是有原因的,但这在很大程度上取决于客户端行为。@cnettel-Umm,KeepAlive的定义是对多个请求使用一个TCP连接。因此,客户端端口总是相同的!此外,我没有看到交叉连接尝试-您能详细说明您在哪里看到它们吗?我看到一个来自端口59712的连接创建、使用和销毁了数据包1-12,然后一个来自端口59714数据包13-24,然后一个来自59716数据包25-。尽管如此,我还是添加了有关如何测试的信息:使用ab和curl。您需要在响应中发送一个内容长度标题。@EJP我的印象是,我正在he.sendResponseHeaders200,FILE.length;行中设置一个标题;。查看wireshark中的数据包细节或curl-v的输出http://localhost:30006/ 表明它已经存在了。您能否验证设置内容长度标题是否可以解决此问题?如果是这样的话,请写一个答案。@pihag好吧,这就是我的观点。即使您使用KeepAlive,也很可能有多个客户端处于活动状态。在我看来,根据单个threadad客户机能够对服务器产生什么影响来测试吞吐量是一个相当奇怪的场景。现在仔细一看,我错在交错上了。非常感谢!事实上,用-sun.net.httpserver.nodelay=true启动java可以解决有keepalive的30k请求/秒、没有keepalive的8k请求/秒的问题。当然,HTTP服务器应该在发送完整响应后刷新;在发送响应之前,不会有来自任何一方的流量。我现在正在研究在代码中而不是在JVM配置中强制此行为的选项;另一种方法是使用反射将sun.net.httpserver.ServerConfig中的静态变量noDelay设置为true,前提是该变量已经初始化并且更改系统属性无效。@OlegKurbatov谢谢!我对这个答案给予赏金并接受了。