闪烁的 HttpClient 有时会抛出 IOException

Itc*_*chy 0 java-http-client java-19

java.net.http.HttpClient.newHttpClient()在 Java 19 (Temurin) 下使用,并sendAsync(...)在同一实例上执行来自不同步骤的请求。我认为这是可以的,正如 javadoc 所说:

一旦构建,HttpClient 就是不可变的......

但是,某些请求会失败并显示以下信息:

java.io.IOException: HTTP/1.1 header parser received no bytes
Run Code Online (Sandbox Code Playgroud)

奇怪的是,这取决于我的请求的速度:

  • 每 5 秒请求一次:30% 失败
  • 每 3 秒请求一次:0% 失败

我为它写了一个测试:

private final HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://..."))
    .setHeader("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofByteArray("[]".getBytes()))
    .build();

@ParameterizedTest
@ValueSource(ints = {3, 5})
void httpClientTest(int intervalSeconds) throws Exception {
    HttpClient httpClient = HttpClient.newHttpClient();

    httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
    Thread.sleep(Duration.ofSeconds(intervalSeconds));
    httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
    Thread.sleep(Duration.ofSeconds(intervalSeconds));
    httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
    Thread.sleep(Duration.ofSeconds(intervalSeconds));
    httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
    Thread.sleep(Duration.ofSeconds(intervalSeconds));
    httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
}
Run Code Online (Sandbox Code Playgroud)

我已经尝试过以下操作:

  • curl在命令行上执行相同的操作。无论我尝试什么时间间隔,都没有请求失败。所以应该不是服务器的问题。
  • 并行运行多次测试。5 秒间隔仍然失败(然后并行多次)。所以应该不是服务器的问题。
  • HttpClient.newHttpClient()为每个请求创建一个。无论时间间隔如何,请求都不会失败。所以这可能不是服务器的问题,而是服务器的内部状态的问题HttpClient(尽管它声称是不可变的?)。

您知道我可以做什么,而不需要HttpClient为每个请求创建一个新的请求吗?

dan*_*iel 5

以下是记录的答案:java.net.HttpClientHTTP/1.1 默认的 keepAlive 时间很长,比通常服务器配置的时间要长。这通常会导致服务器先于客户端关闭空闲的 HTTP/1.1 连接。如果服务器在客户端尝试重用连接的同时关闭连接,则IOException可能会引发一些问题。

如果此类异常出现得太频繁,应用程序应考虑将客户端中的默认 keepAlive 时间调整为比其连接到的服务器正在使用的时间短的某个值。可以在命令行上指定HttpClient HTTP/1.1 keepAlive 时间的默认值:-Djdk.httpclient.keepalive.timeout=duration-in-seconds

例如,如果服务器配置的 keepAlive 时间为 5 秒,您可以考虑在客户端的 java 命令行上提供-Djdk.httpclient.keepalive.timeout=3或。-Djdk.httpclient.keepalive.timeout=4