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

How to handle HTTP/2 GOAWAY with HttpClient?

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

The GOAWAY frame (type=0x7) is used to initiate shutdown of a connection or to signal serious error conditions.
§ 6.8, RFC 7540


我做了一些研究,发现不仅是 1000 个请求 nginx's default maximum, Cloudfront (related Chromium issue),而且 Discord 也表现出相同的行为。

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

server {
    listen 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);
}

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

...
Image 998 processed
Exception in thread "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;
}

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

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

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

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

遗憾的是,lastStreamId 未在 java.net.http.HttpClient 中浮出水面,因此无法了解并采取适当的措施。

您的选择可能是使用其他支持呈现 lastStreamId 的客户端,或者使用较低级别的 HTTP/2 客户端,您将在其中获得 GOAWAY 框架,因此可以访问lastStreamId.

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

的示例