如何为Spring WebFlux配置Netty连接超时时间

6

我正在运行Spring Cloud Gateway(据我了解是建立在Spring Webflux之上)位于AWS负载均衡器后面,但是我偶尔会收到502错误。经过调查,发现问题与负载均衡器和我的节点之间的连接超时有关。通过一些调查,发现底层Netty服务器有一个默认的超时时间为10秒钟。我使用以下命令确定了这一点...

time nc -vv 10.10.xx.xxx 5100
Connection to 10.10.xx.xxx 5100 port [tcp/*] succeeded!

real    0m10.009s
user    0m0.000s
sys     0m0.000s

虽然我可以将负载均衡器上的idleTimeout设置为不到10秒,但这感觉非常低效。如果可能的话,我想将其保持在30秒以上。相反,我想要增加netty服务器上的连接超时时间。我已经尝试在我的application.yml文件中设置server.connection-timeout属性...

server:
  connection-timeout: 75000

通过指定秒数,您可以使计时器更加精确。

server:
  connection-timeout: 75s

但是这对我运行time命令来查看连接持续时间的超时时间没有任何影响,它仍然在10秒钟结束...

time nc -vv 10.10.xx.xxx 5100
Connection to 10.10.xx.xxx 5100 port [tcp/*] succeeded!

real    0m10.009s
user    0m0.000s
sys     0m0.000s

我在这里缺少什么?

2个回答

9
server.connection-timeout配置键目前不支持Netty服务器,但我已经提出了spring-boot#15368以解决这个问题。
连接超时是关于等待建立连接的最长时间。如果您想自定义读/写超时,那么可以使用不同的选项。您可以添加一个ReadTimeoutHandler,如果服务器在配置的持续时间内未从客户端接收到数据,则关闭连接。 同样适用于WriteTimeoutHandler,但此时为服务器向客户端写入数据。
以下是完整的示例:
@Configuration
public class ServerConfig {

    @Bean
    public WebServerFactoryCustomizer serverFactoryCustomizer() {
        return new NettyTimeoutCustomizer();
    }

    class NettyTimeoutCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {

        @Override
        public void customize(NettyReactiveWebServerFactory factory) {
            int connectionTimeout = //...;
            int writetimeout = //...;
            factory.addServerCustomizers(server -> server.tcpConfiguration(tcp ->
                    tcp.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout)
                            .doOnConnection(connection ->
                                    connection.addHandlerLast(new WriteTimeoutHandler(writetimeout)))));
        }
    }

}

现在回到你的问题,我已经使用以下控制器测试了该配置:

@RestController
public class TestController {

    @GetMapping(path = "/", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> textStream() {
        return Flux.interval(Duration.ofSeconds(5)).map(String::valueOf);
    }
}

只要间隔时间短于配置的写入超时时间,服务器就不会关闭连接。您可以使用httpie和以下命令http localhost:8080/ --stream --timeout 60进行验证。
我已在本地测试过此netcat命令,目前还未出现超时情况。
time nc -vv 192.168.0.28 8080
192.168.0.28 8080 (http-alt) open
^CExiting.
Total received bytes: 0
Total sent bytes: 0
nc -vv 192.168.0.28 8080  0.01s user 0.00s system 0% cpu 2:36.53 total

也许这是在操作系统层面进行的配置,或者可能是网络设备配置为关闭此类连接?我刚刚看到您添加了spring-cloud-gateway标签 - 也许这与该项目有关?

我已经实现了这个,但是我仍然得到相同的结果。 CONNECT_TIMEOUT 属性是我应该使用的属性吗? 本质上,根据我读过的所有内容,我需要相当于 Apache KeepAlive 的东西来解决这种情况。 在 Netty 中是否有一个可以设置 KeepAlive 的属性? - brunch
我已经编辑了我的回答(代码片段是错误的)。但我无法重现你的问题。请参阅我编辑后答案的最后一节。 - Brian Clozel
我需要在不进行调用的情况下保持与运行Netty的服务器端口的连接。Tomcat默认情况下可以做到这一点,保持连接时间为60秒。这基本上就是我所需要的。 - brunch
正如我在答案中提到的那样,我的示例应用程序在本地没有得到相同的结果。nc命令从未超时。 - Brian Clozel
Brian,抱歉回复晚了。我们实施了一个解决方法来度过一段时间。我有一个示例展示我的问题。我想为此打开一个问题,但不确定在哪里创建它。我应该将其放在Spring项目、Spring Boot中吗? - brunch
显示剩余5条评论

1

https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html中的spring文档目前将server.connection-timeout定义为“连接器在关闭连接之前等待另一个HTTP请求的时间。”

对于Netty,该属性当前的作用与此不同。现在,该属性控制TCP连接握手超时,这是完全不同的事情。

有关此更多信息,请参见https://github.com/spring-projects/spring-boot/issues/18473,其中提供了实际配置空闲/保持活动超时的示例。

具体来说,您可以使用类似以下内容的代码:

import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

@Configuration
public class NettyConfig {

    @Bean
    public ReactiveWebServerFactory reactiveWebServerFactory(@Value("${server.netty.idle-timeout}") Duration idleTimeout) {
        final NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory();
        factory.addServerCustomizers(server ->
                server.tcpConfiguration(tcp ->
                        tcp.bootstrap(bootstrap -> bootstrap.childHandler(new ChannelInitializer<Channel>() {
                            @Override
                            protected void initChannel(Channel channel) {
                                channel.pipeline().addLast(
                                        new IdleStateHandler(0, 0, idleTimeout.toNanos(), NANOSECONDS),
                                        new ChannelDuplexHandler() {
                                            @Override
                                            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
                                                if (evt instanceof IdleStateEvent) {
                                                    ctx.close();
                                                }
                                            }
                                        }
                                );
                            }
                        }))));
        return factory;
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接