Как проверить внутри Ktor, что Netty действительно запущен?

Мне нужно выполнить некоторую инициализацию моего приложения Ktor, но я хочу сделать это только после того, как Netty будет готова принимать соединения. С другой стороны, я не хочу, чтобы такая инициализация происходила, если Netty не удалось запустить (например, с типичным «адресом, который уже используется»).

Я применил простой подход (см. ниже), но интересно, можно ли сделать его менее безобразным?

Сначала я сохраняю ссылку на NettyApplicationEngine:

embeddedServer = embeddedServer(Netty, port, module)

Затем я использую поле channels из NettyApplicationEngine, чтобы определить его состояние:

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)
            }
        }
    }

person Nikita Tukkel    schedule 03.05.2019    source источник


Ответы (1)


Попробовав несколько разных подходов, я завершил самопроверкой, которая просто отправляет HTTP-запрос на мою собственную конечную точку, и если и HttpClient, и обработчик маршрута работают успешно, я считаю, что Netty готова.

Сначала я регистрирую Routing.RoutingCallFinished событие с NettyApplicationEngine.environment.monitor (позже я dispose создал обработчик).

Затем я повторяю все NettyApplicationEngine.environment.connectors и создаю Deferred, которые будут завершены из обработчика RoutingCallFinished. Кроме того, я запускаю асинхронные сопрограммы, которые будут проверять соответствующие конечные точки с помощью HttpClient.

После этого я awaitAll на этих Deferred (и на Deferred из обработчика событий ApplicationStarted тоже).

person Nikita Tukkel    schedule 13.05.2019