如何在 Ktor 内部检查 Netty 是否真的启动了?

How to check inside Ktor that the Netty is actually started?

我需要对我的 Ktor 应用程序进行一些初始化,但我只想在 Netty 准备好接受连接后进行初始化。另一方面,如果 Netty 无法启动(例如典型的 "address already in use"),我不希望发生这种初始化。

我实现了直接的方法(见下文),但我想知道是否可以不那么丑陋的方法

首先我保存对 NettyApplicationEngine 的引用:

embeddedServer = embeddedServer(Netty, port, module)

然后我使用来自 NettyApplicationEngine 的 channels 字段来确定它的状态:

private fun NettyApplicationEngine.channelsReady(): Boolean {
    val channelsField = this::class.members.find { it.name == "channels" }!!
    channelsField.isAccessible = true
    val channels = channelsField.call(this) as List<Channel>?
    return !channels.isNullOrEmpty() && channels.all { it.isActive }
}

最后,我捕捉到 ApplicationStarted 事件并旋转直到通道准备就绪:

environment.monitor.subscribe(ApplicationStarted) {
        thread(start = true, name = "real netty postinit") {
            for (i in 1..100) {
                TimeUnit.MILLISECONDS.sleep(100)
                if (embeddedServer.channelsReady()) break
            }

            if (embeddedServer.channelsReady()) {
                // Initialization here
            } else {
                // Server didn't start
                embeddedServer.stop(1, 1, TimeUnit.SECONDS)
            }
        }
    }

在尝试了几种不同的方法后,我得出了一个 self-test 结论,它只是将 HTTP 请求发送到我自己的端点,如果 HttpClient 和路由处理程序都成功执行,我认为 Netty 已准备就绪。

首先,我使用 NettyApplicationEngine.environment.monitor 注册了 Routing.RoutingCallFinished 事件(稍后我 dispose 创建了处理程序)。

然后我迭代所有 NettyApplicationEngine.environment.connectors 并创建将从 RoutingCallFinished 处理程序完成的 Deferred。此外,我启动了异步协程,它将使用 HttpClient 检查相应的端点。

之后,我 awaitAll 在那些 Deferred 上(以及 Deferred 来自 ApplicationStarted 事件处理程序)。