在 ktor 请求处理程序中启动协程

Posted

技术标签:

【中文标题】在 ktor 请求处理程序中启动协程【英文标题】:Launching coroutine in ktor request handler 【发布时间】:2021-12-10 06:07:31 【问题描述】:

我想立即响应客户端并通过启动协程在后台处理请求。首先我尝试了以下解决方案:

suspend fun PipelineContext<Unit, ApplicationCall>.handleTest(value: Unit) 
    // insert calls to suspending functions here

    launch 
        repeat(10000) 
            println("Executing background task $it.")
            delay(1000)
        
    

    call.respond("Executing the task in background")


routing 
    get("/test", PipelineContext<Unit, ApplicationCall>::handleTest)

这按预期工作。它立即返回并执行后台任务。 不过,IntelliJ IDE 给了我以下警告:

Ambiguous coroutineContext due to CoroutineScope receiver of suspend function 

我知道这个警告的含义以及它发生的原因,所以我试图找到解决这个问题的方法:

suspend fun handleTest(context: PipelineContext<Unit, ApplicationCall>) 
    // insert calls to suspending functions here

    context.launch 
        repeat(10000) 
            println("Executing background task $it.")
            delay(1000)
        
    

    context.call.respond("Executing the task in background")


routing 
    get("/test") 
        handleTest(this)
    

这段代码也可以按预期工作,但是在阅读本文https://elizarov.medium.com/explicit-concurrency-67a8e8fd9b25 时,我觉得它是错误的。作者解释说,除非将其包装在新的 coroutineScope 中,否则不应在挂起函数中启动协程。

我很好奇,尝试内联handleTest

routing 
    get("/test") 
        // insert calls to suspending functions here

        launch 
            repeat(10000) 
                println("Executing background task $it.")
                delay(1000)
            
        

        call.respond("Executing the task in background")
    

这也按预期工作,甚至警告也消失了。但是,该构造在理论上仍然与第一个解决方案中的相同。

我的问题的正确解决方案是什么?

【问题讨论】:

【参考方案1】:

我建议创建一个新的协程作用域,以明确coroutineContext 的取用位置,因为CoroutineScope 和suspend 函数都有它。

suspend fun PipelineContext<Unit, ApplicationCall>.handleTest(value: Unit) = coroutineScope 
    // insert calls to suspending functions here
    launch 
        repeat(10000) 
            println("Executing background task $it.")
            delay(1000)
        
    

    call.respond("Executing the task in background")

【讨论】:

【参考方案2】:

你可以使用 CoroutineScope:

routing 
get("/test") 
    // insert calls to suspending functions here

    CoroutineScope(Job()).launch 
        repeat(10000) 
            println("Executing background task $it.")
            delay(1000)
        
    

    call.respond("Executing the task in background")

它将创建一个新的范围解耦处理程序上下文。

【讨论】:

以上是关于在 ktor 请求处理程序中启动协程的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 使用协程处理改造请求

Android之网络请求通过协程+okhttp的没有做网络异常处理导致程序奔溃问题

在 Ktor 中访问请求中的路由路径字符串

有没有办法在 Ktor 中指定通配符路由?

协程怎么知道什么时候切换回来?:事件驱动

事件驱动与异步IO