使用Http/2时Reactor Netty客户端ReadTimeoutException导致PrematureClos



我正在使用一个reactor netty客户端,并通过alpn设置http/2。当我通过.doOnConnected(…(挂钩设置ReadTimeoutHandler时,我最终会得到以下异常:

Jan 09, 2021 6:40:35 PM io.netty.channel.DefaultChannelPipeline onUnhandledInboundException
WARNING: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.timeout.ReadTimeoutException

这看起来很好,响应也很好,所以我真的不确定那里发生了什么,但当我开始努力驱动我的客户时,尤其是在gatling时,我开始看到通过PrematureCloseException的失败响应,这在生产环境中是不可接受的。一旦我移除ReadTimeoutHandler,这种行为就会消失。

我不确定这里发生了什么,也不确定是否因为Http/2而设置错误。据我所知,这是扩展客户端和设置ReadTimeoutHandler的推荐方法。这在Http/1.1上运行得很好,我不确定Http/2上发生了什么变化。

下面介绍了如何设置客户端来重现这一点——只需使用此客户端来访问您最喜欢的http/2端点。

我使用的是projectreactor bom版本2020.0.2


public static HttpClient createClient() {
final String[] alpnProtocols =
new String[]{ApplicationProtocolNames.HTTP_2};
// if the application protocol is set to null the netty code will set it to
// JdkDefaultApplicationProtocolNegotiator.INSTANCE (which is package protected)
final ApplicationProtocolConfig APPLICATION_PROTOCOL_CONFIG =
new ApplicationProtocolConfig(
ApplicationProtocolConfig.Protocol.ALPN,
// NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers.
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
// ACCEPT is currently the only mode supported by both OpenSsl and JDK providers.
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
alpnProtocols);
final String[] SUPPORTED_PROTOCOLS =
new String[]{"TLSv1.3"}; // note that http/2 only works with TLSv1.3 or TLSv1.2
// a null for non http/2 will result in the default cipher suites for http1.1
final List<String> CIPHER_SUITES =
Arrays.asList(new String[]{ "TLS_AES_256_GCM_SHA384" });

try {
System.setProperty("io.netty.handler.ssl.noOpenSsl", "false"); // lazy way to set this up
SslContext sslContext = SslContextBuilder
.forClient()
.sslProvider(io.netty.handler.ssl.SslProvider.OPENSSL)
.ciphers(CIPHER_SUITES)
.applicationProtocolConfig(APPLICATION_PROTOCOL_CONFIG)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.protocols(SUPPORTED_PROTOCOLS)
.build();

return HttpClient.create()
.port(3443)
.baseUrl("https://localhost")
.secure(spec -> spec.sslContext(sslContext))
.protocol(HttpProtocol.H2)
.doOnConnected(con -> con.addHandlerLast(
new ReadTimeoutHandler(4_500, TimeUnit.MILLISECONDS)));

} catch (Exception e) {
throw new RuntimeException(e);
}

PrematureCloseException是当Gatling试图在其上进行写入时,远程对等方关闭连接时得到的结果。当重用池保持活动连接时,这是一种非常正常的情况,因为网络不是即时的。

旧版本的加特林并没有完美地处理这个案件。如果你使用的是旧版本,你应该升级(目前最新版本是3.5.0(

最新更新