【问题标题】:how create a coroutine inside a Controller method in order to call a suspend function如何在 Controller 方法中创建协程以调用挂起函数
【发布时间】:2021-03-26 16:25:26
【问题描述】:

目标:我的微服务必须使用另一个 Rest 端点,我正在尝试关注 Coroutines (Async)。

这是我在 Service 中编码以使用另一个休息端点的方式

服务

suspend fun getCoroutine(){

    val someData = getData()

    print(someData)
}

suspend fun getData(): String {

    
    val client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_2)
            .authenticator(Authenticator.getDefault())
            .build();

    val request = HttpRequest.newBuilder()
            .uri(URI.create("http://localhost:3000/employees"))
            .build();

    val response = client.sendAsync(request, BodyHandlers.ofString());
    return response.get().body() // suspend and return String not a Future
}

我想从我的控制器调用“暂停有趣的 getCoroutine()”方法

package com.tolearn.endpoint

import com.tolearn.DemoGrpcKafkaReply
import com.tolearn.DemoGrpcKafkaRequest
import com.tolearn.DemoGrpcKafkaServiceGrpc
import com.tolearn.service.DemoService
import io.grpc.stub.StreamObserver
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton


@Singleton
class DemoEndpoint : DemoGrpcKafkaServiceGrpc.DemoGrpcKafkaServiceImplBase(){

    @Inject
    lateinit var demoService: DemoService

    override fun send(request: DemoGrpcKafkaRequest?, responseObserver: StreamObserver<DemoGrpcKafkaReply>?) {

        demoService.getCoroutine()

    }
}

我是 Coroutine 的新手。我了解到挂起函数只能从 anotehr 挂起函数或协程中调用,因此,就我而言,我想创建一个协程。谷歌搜索后我尝试了

override fun send(request: DemoGrpcKafkaRequest?, responseObserver: StreamObserver<DemoGrpcKafkaReply>?) {

    val tryingCoroutine = runBlocking { demoService.getCoroutine() }

但是runBlocking无法解决。

我也尝试过基于docs reference 并且 lauch 也无法解决

override fun send(request: DemoGrpcKafkaRequest?, responseObserver: StreamObserver<DemoGrpcKafkaReply>?) {
    
    launch( demoService.getCoroutine() ) { // not confined -- will work with main thread

    }

这里是 build.gradle

plugins {
    id("org.jetbrains.kotlin.jvm") version "1.4.10"
    id("org.jetbrains.kotlin.kapt") version "1.4.10"
    id("org.jetbrains.kotlin.plugin.allopen") version "1.4.10"
    id("com.github.johnrengelman.shadow") version "6.1.0"
    id("io.micronaut.application") version "1.2.0"
    id("com.google.protobuf") version "0.8.13"
}

version = "0.1"
group = "com.tolearn"

repositories {
    mavenLocal()
    jcenter()
    mavenCentral()

}

micronaut {
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("com.tolearn.*")
    }
}

dependencies {
    implementation("io.micronaut:micronaut-validation")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
    implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
    implementation("io.micronaut:micronaut-runtime")
    implementation("io.micronaut.grpc:micronaut-grpc-runtime")
    implementation("javax.annotation:javax.annotation-api")
    implementation("io.micronaut.kafka:micronaut-kafka")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:'1.4.2")

    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin")
    testImplementation("io.micronaut:micronaut-http-client")
}


application {
    mainClass.set("com.tolearn.ApplicationKt")
}

java {
    sourceCompatibility = JavaVersion.toVersion("11")
}

tasks {
    compileKotlin {
        kotlinOptions {
            jvmTarget = "11"
        }
    }
    compileTestKotlin {
        kotlinOptions {
            jvmTarget = "11"
        }
    }


}

sourceSets {
    main {
        java {
            srcDirs("build/generated/source/proto/main/grpc")
            //srcDirs 'build/generated/source/proto/main/grpckt'
            srcDirs("build/generated/source/proto/main/java")
        }
    }
}

protobuf {
    protoc { artifact = "com.google.protobuf:protoc:3.14.0" }
    plugins {
        grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.33.1" }
        //grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:1.0.0" }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
            //grpckt {}
        }
    }
}

主要问题:我必须做什么才能从控制器的方法中调用挂起函数?第二个问题,我是不是在做一些奇怪的方法来尝试从 Controller 方法调用挂起函数?在这种情况下,我试图利用协程是错误的吗?

***编辑1

val tryingCoroutine = runBlocking {
    coroutineScope { // Creates a coroutine scope
        launch {
            demoService.getCoroutine()
            println("Task from nested launch")
        }
    }
}
println(tryingCoroutine.isCompleted)

【问题讨论】:

    标签: kotlin kotlin-coroutines coroutine


    【解决方案1】:

    您需要添加kotlinx-coroutines-core 依赖来解析范围(runBlocking/launch)。

    这里是maven repo的链接: https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core

    范围文档:
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html

    一旦您将作用域添加到您的项目中,您应该能够在非暂停块中运行暂停的函数。此外,您还可以使用CoroutineExceptionHandler 来处理错误。 https://kotlinlang.org/docs/reference/coroutines/exception-handling.html#coroutineexceptionhandler

    从技术上讲,控制器应该将任何长时间运行的操作移交给另一个线程并返回适当的响应。所以你在做任何奇怪的事情。

    【讨论】:

    • 关于我在我的 gradle 中的依赖项,但不幸的是在版本之前有一个错字,我没有得到指向它的错误(implementation("org.jetbrains.kotlinx:kotlinx-coroutines -core:'1.4.2")。我阅读了文档并在 Google 上搜索,但我没有确切了解如何创建 Scope。您能举一个简单的例子吗?我现在可以从我的控制器调用挂起函数但是有了这个非常简单的方法: val TryCoroutine = runBlocking { demoService.getCoroutine() }。不确定我的方向是否正确。最后你的最后一个链接弹出 404
    • 基于kotlinlang.org/docs/reference/coroutines/… 我创建了上面的范围(请参阅上面的编辑1)。正确吗?它在 IntelliJ 调试中工作,但我是否在我的上下文中正确使用了协程作用域(从控制器调用挂起函数)?我应该如何正确处理这里的异常?我是否必须测试它是否已完成?
    • 这将帮助您创建自定义范围并处理取消。 kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/…
    猜你喜欢
    • 2019-05-30
    • 2020-06-27
    • 2021-06-05
    • 2019-05-24
    • 2021-11-03
    • 2019-09-09
    • 2020-11-07
    • 2020-11-18
    相关资源
    最近更新 更多