Search code examples
kotlinnettyspring-webfluxspring-webclientreactor-netty

How to close TCP connection after some inactivity in Reactor Netty WebClient?


I would like to close the TCP connection after some time of inactivity. By inactivity, I mean that TCP connection is not being used for a certain amount of time.

I thought that I will use connection handlers for that purpose.

private val webClient = webClientBuilder
    .build()
    .mutate()
    .filters(MutableList<ExchangeFilterFunction>::clear)
    .clientConnector(
        ReactorClientHttpConnector(
            HttpClient.from(
                TcpClient.create()
                    .option(CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT)
                    .doOnConnected { connection ->
                        connection
                            .addHandlerLast(
                                IdleStateHandler(
                                    SOCKET_INACTIVITY_TIMEOUT,
                                    SOCKET_INACTIVITY_TIMEOUT,
                                    0
                                )
                            )
                            .addHandlerLast(CloseIdleConnectionHandler())
                            .addHandlerLast(ReadTimeoutHandler(READ_TIMEOUT))
                            .addHandlerLast(WriteTimeoutHandler(WRITE_TIMEOUT))
                    }
            )
        )
    )
    .build()

companion object {
    const val SOCKET_INACTIVITY_TIMEOUT = 20
    const val CONNECT_TIMEOUT = 5_000
    const val READ_TIMEOUT = 5
    const val WRITE_TIMEOUT = 5
}

private class CloseIdleConnectionHandler : ChannelDuplexHandler() {
    override fun userEventTriggered(ctx: ChannelHandlerContext, evt: Any) {
        if (evt is IdleStateEvent) {
            ctx.disconnect()
        }
    }
}

The problem is that CloseIdleConnectionHandler does not get called after SOCKET_INACTIVITY_TIMEOUT.

Am I doing something wrong or I misuse IdleStateHandler?


Solution

  • You can use the ConnectionProvider.builder to configure these settings

            ConnectionProvider connectionProvider =
                ConnectionProvider.builder("my-connection-pool")
                        .maxConnections(100)
                        .pendingAcquireTimeout(Duration.ofMillis(100))
                        // this is the setting you're after
                        .maxIdleTime(Duration.ofMillis(1000))
                        .maxLifeTime(Duration.ofMillis(5000))
                        .build()
    

    You would then pass in your connection provider in when constructing your tcp client

    TcpClient.create(connectionProvider)
             .options(..... // other custom configuration
    

    See javadoc