Spring REST 端点中的 Kotlin 协程

Jav*_*ide 3 concurrency kotlin spring-boot kotlin-coroutines

给定一个 REST 端点和两个异步协程,每个异步协程返回一个整数,我希望该端点返回它们的总和。这两个函数(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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Your result: DeferredCoroutine{Completed}@1c1fe804
Final Result: DeferredCoroutine{Completed}@48a622de
Run Code Online (Sandbox Code Playgroud)

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

我希望端点在大约 3 秒内返回“100”。我怎样才能实现这个目标?

bro*_*oot 7

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

  • 仅用于runBlocking()使用runBlocking()其中的另一个。
  • 使用async()并立即调用await()它(在summing()) - 它什么也不做。
  • funA().await()确实没有funB().await()任何意义。这些函数返回整数,但不能await()返回已获取的整数。
  • 一般来说,使用的代码比需要的多得多。

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

runBlocking {
    val a = async { funA() }
    val b = async { funB() }
    a.await() + b.await()
}
Run Code Online (Sandbox Code Playgroud)

或者:

runBlocking {
    listOf(
        async { funA() },
        async { funB() },
    ).awaitAll().sum()
}
Run Code Online (Sandbox Code Playgroud)

或者(稍微短一些,但我认为可读性较差):

runBlocking { 
    val a = async { funA() }
    funB() + a.await()
}
Run Code Online (Sandbox Code Playgroud)

而且,runBlocking()也并不理想。我相信 Spring 支持协程,因此最好使用summing()函数suspend并使用coroutineScope()runBlocking(),这样代码就不会阻塞任何线程。