【问题标题】:How consume multiple endpoints at same time using Kotlin Coroutine in a BackEnd Controller如何在后端控制器中使用 Kotlin 协程同时使用多个端点
【发布时间】:2021-05-21 21:19:43
【问题描述】:

上下文:我发现很少有教程解释如何同时使用 Kotlin 的多个端点,但它们基于 Android,在我的情况下它是一个后端应用程序。我有一些使用 CompleteableFuture 的经验,但我认为我应该使用 Coroutine,因为它是 Kotlin 并且没有 Spring 依赖项。

根据一些建议,我到达了

@Singleton
class PersonEndpoint()
    {

    @Inject
    lateinit var employeClient: EmployeClient

    override suspend fun getPersonDetails(request: PersonRequest): PersonResponse {
        var combinedResult: String

        GlobalScope.launch {
            val resultA: String
            val resultB: String

            val employeesA = async{ employeClient.getEmployeesA()}

            val employeesB = async{ employeClient.getEmployeesB()}

            try{

                combinedResult = employeesA.await() + employeesB.await()

                print(combinedResult)

            } catch (ex: Exception) {
                ex.printStackTrace()
            }

        // ISSUE 1
        if I try add return over here it is not allowed. 
        I understand it is working how it is designed to work: GlobalScope is running in different thread

        }
        
    // ISSUE 2
    if I try return combinedResult over here combinedResult isn't initialized.
    I understand it is working how it is designed to work: GlobalScope is running in different thread and I can
    debug and see that return over here executes earlier than employeesA.await = employeesB.await

}

那么,如何在返回客户端之前执行 combineResult = employeesA.await() + employeesB.await()?

*** 在丹尼斯/回答之后编辑

@Singleton
class CustomerEndpoint(){

    fun serve(): Collection<Int> {
        return runBlocking {
            async {
                getItemDouble(1)
            }
            async {
                getItemTriple(1)
            }
        }.map { it.await() }
    }

suspend fun getItemDouble(i: Int): Int {
    delay(1000)
    return i * 2
}

suspend fun getItemTriple(i: Int): Int {
    delay(1000)
    return i * 3
}

override suspend fun getPersonDetails(request: PersonRequest): PersonResponse {

    val result = serve()
    println("Got result $result")
    
    ...
}

【问题讨论】:

    标签: java kotlin async-await kotlin-coroutines


    【解决方案1】:
    import kotlinx.coroutines.async
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.runBlocking
    import kotlin.system.measureTimeMillis
    
    fun main() {
        val durationMs = measureTimeMillis {
            val result = serve()
            println("Got result $result")
        }
        println("The processing is done in $durationMs ms")
    }
    
    fun serve(): Collection<Int> {
        return runBlocking {
            (1..2).map {
                async {
                    getItem(it)
                }
            }.map { it.await() }
        }
    }
    
    suspend fun getItem(i: Int): Int {
        delay(1000) // Emulate item retrieval work
        return i * 2
    }
    

    请注意,这里有两个嵌套调用 - getItem(1)getItem(2)。我们可以看到它们是并行执行的,总运行时间约为 1 秒。


    于 2021 年 8 月 5 日编辑

    private suspend fun myMethod(): List<Any> {
        return runBlocking {
            listOf(
                async { method1() },
                async { method2() }
            ).map { it.await() }
        }
    }
    

    method1 和 method2 是调用不同端点的方法。

    【讨论】:

    • 丹尼斯,谢谢。如果必须并行调用不同的方法,你会怎么做?我在上面编辑了我的问题以举例说明我的问题。
    • 它没有改变任何东西——我们仍然有两个并行运行的调用。您可以运行此代码并确保它仍然在 ~1 秒内执行 - gist.github.com/denis-zhdanov/faf19c16ba295247d72e5993d6d1ac00
    • 欢迎您,吉姆!协程的一个很酷的地方是它们提供了所谓的结构化并发,即如果一个协程失败,当前的协程上下文也会失败(所有兄弟协程都被取消)。由于协程上下文是分层的(例如,这里我们在runBlocking() 创建一个根上下文,每个async() 创建一个子上下文),整个执行结束。异常处理像往常一样通过 try/catch 完成。例如,您可能想将一个子步骤(可能带有子子步骤)视为可选,在此处添加一个额外的 try/catch 并且不要因异常而停止
    • 以防万一,一个关于结构化并发的链接 - elizarov.medium.com/structured-concurrency-722d765aa952
    • 再注意一点,正如你所说,协程将调用不同的端点,即 I/O,这就是为什么最好使用Dispatchers.IO。请随时查看对应的documentation chapter
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-21
    • 1970-01-01
    • 2015-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多