我正在使用 ServerSocket 在 Java 中实现我自己的 HTTP 服务器。我想开始实现管道,所以我编写了一个测试,我预计首先会失败。
void sendMultipleRequestsOnSameConnection() throws URISyntaxException, InterruptedException, ExecutionException {
HttpRequest httpRequest = HttpRequest.newBuilder()
.POST(BodyPublishers.ofString("LOREM"))
.uri(new URI("http://127.0.0.1/wait2"))
.version(Version.HTTP_1_1)
.build();
HttpRequest httpRequest2 = HttpRequest.newBuilder()
.POST(BodyPublishers.ofString("LOREM"))
.uri(new URI("http://127.0.0.1/wait"))
.version(Version.HTTP_1_1)
.build();
CompletableFuture<HttpResponse<String>> future1 = httpClient.sendAsync(httpRequest, BodyHandlers.ofString());
Thread.sleep(500);
CompletableFuture<HttpResponse<String>> future2 = httpClient.sendAsync(httpRequest2, BodyHandlers.ofString());
我的服务器每次接受新连接时都会记录日志。运行此测试后,我注意到服务器正在接受 2 个连接,我只期望 1 个连接,因为我有另一个测试执行类似的操作,并且它只打开 1 个与服务器的连接。
这是按预期工作的测试:
@Test
void sendMultipleRequestsOnSameConnection() throws URISyntaxException, IOException, InterruptedException {
HttpRequest httpRequest = HttpRequest.newBuilder()
.POST(BodyPublishers.ofString("LOREM"))
.uri(new URI("http://127.0.0.1/"))
.version(Version.HTTP_1_1)
.build();
String responseBody1 = httpClient.send(httpRequest, BodyHandlers.ofString()).body();
String responseBody2 = httpClient.send(httpRequest, BodyHandlers.ofString()).body();
Assertions.assertEquals("HELLO WORLD", responseBody1);
Assertions.assertEquals("HELLO WORLD", responseBody2);
}
知道为什么会发生这种情况吗?有没有办法强制 HTTP 客户端重用第一个请求中的相同连接?如果没有,那么是否有任何外部 Java HTTP 客户端允许这样做,或者我需要直接使用 Socket 来确保只打开 1 个连接?
这是存储库的链接:https://github.com/dev-rifaii/http-from-scratch/tree/pipelined
Java的HttpClient支持连接重用,但并不保证,特别是在异步情况下。影响连接重用的主要因素包括:
Java HttpClient 自动管理连接,但不保证连接始终被重用,主要是在使用 sendAsync 时,根据时间或其他内部因素,在您的测试中,两个请求发送有延迟,这可能会导致 HttpClient决定应该打开一个新连接而不是重用现有连接。因此,如果您可能需要强制重用连接,您还可以在请求中显式设置 Connection: keep-alive 标头。
示例代码
HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.build();
HttpRequest httpRequest = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString("LOREM"))
.uri(new URI("http://127.0.0.1/wait"))
.header("Connection", "keep-alive")
.build();
HttpResponse<String> response1 = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response2 = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
如果这仍然不能满足您对连接重用的需求,请考虑使用更强大的 HTTP 客户端,例如 Apache HttpClient、OkHttp 或 Spring WebClient,它可以更好地控制连接管理