如何使用HttpClient处理HTTP/2 GOAWAY? [英] How to handle HTTP/2 GOAWAY with HttpClient?

查看:974
本文介绍了如何使用HttpClient处理HTTP/2 GOAWAY?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to continuously send GET and POST requests to a REST API every few minutes. The issue is that after exactly 1000 requests I receive a GOAWAY frame (and an IOException):

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

The GOAWAY frame (type=0x7) is used to initiate shutdown of a connection or to signal serious error conditions.
HTTP/2 spec


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


I did a fair bit of research and found that not only is 1000 requests nginx's default maximum, Cloudfront (related Chromium issue) and Discord also exhibit the same behavior.

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

I tried to reproduce this problem with a local nginx server with the default HTTP/2 configuration:


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错误:

And after approximately 1000 requests, I get a GOAWAY error as expected:


...
Image 998 processed
Exception in thread "main" java.io.IOException: /127.0.0.1:49259: GOAWAY received


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


My first thought would be to check if the exception message contains the string "GOAWAY" and then retry the request accordingly:

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的帧(在这种情况下,我可能会重试该请求.

My issue with this approach is that the string comparison seems like it may be fragile. Additionally, since all I have is an IOException with a message, I can't differentiate between GOAWAY frames with a genuine error code (in which case I should probably stop sending requests) and those with NO_ERROR (in which case I could probably retry the request).

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

How should I correctly deal with/handle GOAWAY errors (apart from using HTTP/1.1 instead)?

推荐答案

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

A server is entitled to close connections at any time, for any reason.

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

In the HTTP/2 GOAWAY frame there is the indication of what was the last stream processed by the server, so the client can know what stream needs to be resent when a connection is closed.

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

Unfortunately, the lastStreamId is not surfaced in java.net.http.HttpClient, so there is no way to know it and take appropriate actions.

您的替代方法是使用支持lastStreamId的其他客户端,或使用较低级别的HTTP/2客户端,您将在其中使用GOAWAY框架并因此访问lastStreamId.

Your alternative could be to use other clients that support surfacing the lastStreamId, or use a lower level HTTP/2 client where you will have the GOAWAY frame available and therefore access to the lastStreamId.

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

[Disclaimer, I am the Jetty HTTP/2 implementer]
Jetty supports a lower level HTTP/2 client that you can use for your use case - you may want to give it a try. You can find an example of how to use Jetty's HTTP2Client here.

这篇关于如何使用HttpClient处理HTTP/2 GOAWAY?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆