frevib
05/07/2025, 12:45 PMfun main() = runBlocking {
launch(<http://Dispatchers.IO|Dispatchers.IO>) {
embeddedServer(CIO, port = 8080, module = Application::modules)
.start(wait = true)
}
launch(<http://Dispatchers.IO|Dispatchers.IO>) {
startKafkaConsumer()
}
}
or have the Kafka consumer be a module of Ktor, e.g.:
fun main() {
embeddedServer(CIO, port = 8080, module = Application::modules)
.start(wait = true)
}
fun Application.modules() {
kafkaModule()
ktorModules()
}
fun Application.kafkaModule() {
launch(<http://Dispatchers.IO|Dispatchers.IO>) {
startKafkaConsumer()
}
}
Approach 1 seems more architecturally correct, having both Ktor server and Kafka consumer running in a coroutine on the same level. The 2nd approach makes Ktor managing the lifecycle of the Kafka consumer, which is arguably less good.Aleksei Tirman [JB]
05/08/2025, 7:09 AMfrevib
05/08/2025, 9:29 AMlaunch
are in the same coroutine scope. We can wait for them to complete with them with joinAll(ktorJob, consumerJob)
, and if one of the Jobs throws an exception, the whole scope is cancelled.frevib
05/08/2025, 9:30 AMfrevib
05/08/2025, 9:30 AM