如何使用 HttpClient 处理 HTTP/2 GOAWAY?

Moi*_*ira 10 java http2 java-http-client java-11

我试图每隔几分钟不断地向 REST API 发送 GET 和 POST 请求。问题是,在恰好 1000 个请求之后,我收到了一个GOAWAY帧(和一个IOException):

GOAWAY 帧(类型=0x7)用于启动连接关闭或发出严重错误情况的信号。
HTTP/2 规范


我做了一些研究,发现 1000 个请求不仅是nginx 的默认最大值,Cloudfront(相关的 Chromium 问题)和 Discord 也表现出相同的行为。

我尝试使用具有默认 HTTP/2 配置的本地 nginx 服务器重现此问题:

服务器 {
    听 443 http2 ssl;
    http2_max_requests 1000;
    ...
}
var client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)
        .build();

for (var i = 0; i < 1100; i++) {
    var url = URI.create(String.format("https://localhost/images/test%d.jpg", i));

    var request = HttpRequest.newBuilder().uri(url).build();

    client.send(request, HttpResponse.BodyHandlers.discarding());
    System.out.printf("Image %d processed%n", i);
}
Run Code Online (Sandbox Code Playgroud)

在大约 1000 个请求之后,我收到了GOAWAY预期的错误:

...
图像 998 已处理
线程“main”中的异常 java.io.IOException: /127.0.0.1:49259: GOAWAY received

我的第一个想法是检查异常消息是否包含字符串"GOAWAY",然后相应地重试请求:

try {
    client.send(request, HttpResponse.BodyHandlers.discarding());
} catch (IOException e) {
    if (e.getMessage().contains("GOAWAY")) {
        client.send(request, HttpResponse.BodyHandlers.discarding());
    } else throw e;
}
Run Code Online (Sandbox Code Playgroud)

我对这种方法的问题是字符串比较似乎很脆弱。此外,由于我所拥有的只是一个带有消息的 IOException,我无法区分GOAWAY具有真正错误代码的帧(在这种情况下我应该停止发送请求)和那些具有NO_ERROR(在这种情况下我可能会重试请求) .

我应该如何正确处理/处理GOAWAY错误(除了使用 HTTP/1.1)?

sbo*_*det 9

服务器有权随时出于任何原因关闭连接。

在 HTTP/2GOAWAY帧中,有服务器处理的最后一个流的指示,因此客户端可以知道在连接关闭时需要重新发送什么流。

不幸的lastStreamId是, 没有出现在 中java.net.http.HttpClient,所以没有办法知道它并采取适当的行动。

您的选择可以是使用其他客户端支持堆焊lastStreamId,或使用较低级别的HTTP / 2客户端,你将有GOAWAY框架可用,因此访问lastStreamId

[免责声明,我是 Jetty HTTP/2 实施者]
Jetty 支持较低级别的 HTTP/2 客户端,您可以将其用于您的用例 - 您可能想尝试一下。您可以HTTP2Client 在此处找到有关如何使用 Jetty 的示例。