首页 > 解决方案 > 在同一个 Spring Boot 应用程序中使用 Tomcat HTTP 和 Netty UDP

问题描述

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

我将 Spring Boot 与 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 vs 8080),这就是我在日志消息中得到的。所以两台服务器之间有一些重要的交互,我不知道。

我该如何解决它,以便两台服务器将在同一个 JVM/Spring Boot 应用程序中一起运行?

标签: javaspring-bootnetty

解决方案


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

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


推荐阅读