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

时间:2019-03-10 11:42:36

标签: java http2 java-11 java-http-client

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

  

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


我做了很多研究,发现不仅有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的帧(在这种情况下,我可能会重试该请求。

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

1 个答案:

答案 0 :(得分:1)

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

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

不幸的是,lastStreamId中没有出现java.net.http.HttpClient,因此无法知道它并采取适当的措施。

您的替代方法可能是使用支持lastStreamId堆焊的其他客户端,或者使用较低级别的HTTP / 2客户端,您将在其中使用GOAWAY框架并因此访问{{1 }}。

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