启动长时间运行的任务,然后立即发送HTTP响应



使用ktor HTTP服务器,我想启动一个长时间运行的任务,并立即向调用客户端返回一条消息。该任务是自给自足的,它能够在数据库中更新其状态,并且单独的HTTP调用返回其状态(即进度条(。

我似乎不能做的只是在后台启动任务并做出回应。我所有的响应尝试都在等待长时间运行的任务完成。我已经尝试过runBlockingcoroutineScope的许多配置,但没有一个对我有效

// ktor route
get("/launchlongtask") {
val text: String = (myFunction(call.request.queryParameters["loops"]!!.toInt()))
println("myFunction returned")
call.respondText(text)
}
// in reality, this function is complex... the caller (route) is not able to 
// determine the response string, it must be done here
suspend fun myFunction(loops : Int) : String {
runBlocking {
launch {
// long-running task, I want to launch it and move on
(1..loops).forEach {
println("this is loop $it")
delay(2000L)
// updates status in db here
}
}
println("returning")
// this string must be calculated in this function (or a sub-function)
return@runBlocking "we just launched $loops loops" 
}
return "never get here" // actually we do get here in a coroutineScope
}

输出:

returning
this is loop 1
this is loop 2
this is loop 3
this is loop 4
myFunction returned

预期:

returning
myFunction returned
(response sent)
this is loop 1
this is loop 2
this is loop 3
this is loop 4

受到Lucas Milotich这个答案的启发,我使用了CoroutineScope(Job()),它似乎起了作用:

suspend fun myFunction(loops : Int) : String {
CoroutineScope(Job()).launch { 
// long-running task, I want to launch it and move on
(1..loops).forEach {
println("this is loop $it")
delay(2000L)
// updates status in db here
}
}
println("returning")
return "we just launched $loops loops" 
}

不确定这是否是资源高效的,或者是首选的方法,但我没有看到很多关于这个主题的其他文档。

为了解释问题中的代码问题,问题是使用runBlocking。这是协同程序和的同步世界和异步世界之间的桥梁

"runBlocking的名称意味着运行它的线程。。。在调用期间被阻止,直到runBlocking{…}内的所有协程都完成执行">

(来自Coroutine文档(。

因此,在您的第一个示例中,myFunction直到包含循环的协程完成后才会完成。

正确的方法是在回答中使用CoroutineScope启动长期运行的任务。需要指出的一点是,您只是将Job()作为CoroutineContext参数传递给CoroutineScope构造函数。CoroutineContext包含多个内容;JobCoroutineDispatcherCoroutineExceptionHandler。。。在这种情况下,因为您没有指定CoroutineDispatcher,所以它将使用CoroutineDispatcher.Default。这是为CPU密集型任务而设计的;CPU核的数量(最小为2个(";。这可能是你想要的,也可能不是你想要的。另一种选择是CoroutineDispatcher.IO,它默认有64个线程。

最新更新