Spring REST 端点内的 Kotlin 协程

Kotlin coroutines within a Spring REST endpoint

给定一个 REST 端点和两个异步协程,每个 return 一个整数,我希望这个端点 return 它们的总和。这两个函数(funA 和 funB)应该 运行 并行,这样整个计算应该需要大约 3 秒。 我正在使用 SpringBoot 2.6.3 和 Kotlin Coroutines 1.6.0。这是我的尝试:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class Controllers {

    @GetMapping(
        value = ["/summing"]
    )
    fun summing(): String {

        val finalResult = runBlocking {
            val result = async { sum() }
            println("Your result: ${result.await()}")
            return@runBlocking result
        }
        println("Final Result: $finalResult")
        return "$finalResult"
    }

    suspend fun funA(): Int {
        delay(3000)
        return 10
    }

    suspend fun funB(): Int {
        delay(2000)
        return 90
    }

    fun sum() = runBlocking {
        val resultSum = async { funA().await() + funB().await() }
        return@runBlocking resultSum
    }
}

问题是此代码无法编译,因为 await() 未被识别为有效方法。如果我删除 await() ,这两个函数将依次执行(总时间约为 5 秒),而不是预期结果 (100),我得到:

Your result: DeferredCoroutine{Completed}@1c1fe804
Final Result: DeferredCoroutine{Completed}@48a622de

因此端点 returns“DeferredCoroutine{Completed}@48a622de”。

我希望端点在 ~3 秒内变为 return“100”。我怎样才能做到这一点?

你真的把事情搞砸了 ;-) 你的代码有几个问题:

  • 仅使用 runBlocking() 以在其中使用另一个 runBlocking()
  • 使用 async() 并立即对其调用 await()(在 summing() 中)- 它什么都不做。
  • funA().await()funB().await() 真的没有任何意义。这些函数 return 整数,你不能 await() 已经获得的整数。
  • 通常,使用的代码比需要的多。

解决方案非常简单:使用 runBlocking() 一次跳入协程世界,然后使用 async() 同时启动两个函数:

runBlocking {
    val a = async { funA() }
    val b = async { funB() }
    a.await() + b.await()
}

或者:

runBlocking {
    listOf(
        async { funA() },
        async { funB() },
    ).awaitAll().sum()
}

或者(短一点,但我认为这不太可读):

runBlocking { 
    val a = async { funA() }
    funB() + a.await()
}

此外,runBlocking()也不理想。我相信 Spring 支持协同程序,所以最好使 summing() 函数 suspend 并使用 coroutineScope() 而不是 runBlocking() - 这样代码就赢了'不阻止任何线程。