强制Java内置的HTTP客户端重用连接

问题描述 投票:0回答:1

我正在使用 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 http httpclient
1个回答
0
投票

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,它可以更好地控制连接管理

© www.soinside.com 2019 - 2024. All rights reserved.