在同一个 Spring 引导应用程序中使用 Tomcat HTTP 和 Netty UDP

Using Tomcat HTTP and Netty UDP in same Spring Boot Application

我正在编写一个需要能够侦听 UDP 数据报并公开一些 HTTP 端点的应用程序。

我正在使用 Spring Boot with Web Starter 用于 HTTP 部分和 Netty 用于 UDP。

要设置 UDP,我使用以下代码:

@Configuration
class NettyConfig(
        @Value("${netty.port}")
        val port: Int,
        @Value("${netty.epoll}")
        val epoll: Boolean
) {

    val log = LoggerFactory.getLogger(NettyConfig::class.java)!!

    @Bean
    fun executor() = Executors.newFixedThreadPool(threads)!!

    @Bean
    fun bootstrap(handlerService: HandlerService): Bootstrap {

        val master: EventLoopGroup
        val clazz: Class<out Channel>

        log.info("Starting Netty Server. EPoll enable: $epoll")

        if (epoll) {
            master = EpollEventLoopGroup(1) 
            clazz = EpollDatagramChannel::class.java
        } else {
            master = NioEventLoopGroup(1) 
            clazz = NioDatagramChannel::class.java
        }

        val bootstrap = Bootstrap()
                .group(master)
                .channel(clazz)
                .option(ChannelOption.SO_SNDBUF, 1048576)
                .option(ChannelOption.SO_RCVBUF, 1048576)
                .option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator(2048, 2048, 64 * 2048))
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.SO_BROADCAST, true)
                .handler(object: ChannelInitializer<DatagramChannel>() {
                    override fun initChannel(ch: DatagramChannel) {
                        ch.pipeline().addLast(
                                LineBasedFrameDecoder(1048576),
                                handler(handlerService))
                    }
                })

        val channel = bootstrap.bind(port).sync().channel()
        log.info("Started Netty Server. Listening on {}", port)
        channel.closeFuture().sync()
        log.info("Stopped Netty Server.")
        return bootstrap
    }
...

并且 HTTP 部分由 Spring Starter Web 管理,由 Tomcat 实现。

我想达到的目标:

两个部分都在工作,但不是一起工作。通过反复试验,我遇到了以下问题: 如果我注释掉这一行(即不启动 Netty):

val channel = bootstrap.bind(port).sync().channel()

HTTP 部分工作(而 UDP obv。不)。如果我把它留在原地 - 一切都会开始,但是,任何对 8080 端口的请求都会出现连接被拒绝的错误。

根据我的理解应该没有问题,因为两台服务器都在不同的端口(5432 与 8080)上侦听,这就是我在日志消息中得到的内容。所以我不知道两台服务器之间有一些重要的交互。

我该如何解决,让两台服务器 运行 一起在同一个 JVM/Spring 启动应用程序中?

我怀疑您的应用程序由于 bootstrap @Bean 方法中的 channel.closeFuture().sync() 而未完成启动。由于启动未完成,Tomcat 没有机会启动,因此无法接受任何传入连接。

与其阻止 bean 的创建,不如使用 destroy 方法调用 channel.closeFuture().sync() 作为应用程序上下文关闭处理的一部分。为此,您可以实现 DisposibleBean 或使用 @PreDestroy.

注释方法