簡體   English   中英

Kotlin 協程 Scope:如果在 Controller 端點中使用,是 return@runBlocking 問題

[英]Kotlin Coroutine Scope : Is return@runBlocking an issue if used in Controller Endpoint

目的:假設我正在編寫一個輕量級異步端點客戶端,我想編寫一個使用輕量級協程的端點的端點。

我的背景:第一次嘗試使用 Kotlin 協程。 我最近幾天學習並四處尋找。 我發現很多文章解釋了如何在 Android 中使用協程,我發現很少有其他文章解釋如何在主 function 中使用協程。 不幸的是,我沒有找到解釋如何使用協程對 Controller 端點進行編碼的文章,如果我做的事情不推薦,它在我腦海中響起警鍾。

當前情況:我使用 Coroutines 成功創建了一些方法,但我想知道哪種方法最適合傳統的 GET。 最重要的是,我想知道如何正確處理異常。

主要問題:推薦以下方法中的哪一種,我應該關注哪種異常處理?

相關的次要問題:有什么區別

fun someMethodWithRunBlocking(): String? = runBlocking {
 return@runBlocking ...
}

suspend fun someMethodWithSuspendModifier(): String?{
  return ...
}

下面的所有試探都在工作並返回 json 響應,但我不知道端點方法上的“runBlocking”和返回“return@runBlocking”是否會給我帶來一些負面的缺點。

Controller(端點)

package com.tolearn.controller

import com.tolearn.service.DemoService
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Produces
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.*
import kotlinx.coroutines.runBlocking
import java.net.http.HttpResponse
import javax.inject.Inject

@Controller("/tolearn")
class DemoController {

    @Inject
    lateinit var demoService: DemoService

    //APPROACH 1:
    //EndPoint method with runBlocking CoroutineScope
    //Using Deferred.await
    //Using return@runBlocking
    @Get("/test1")
    @Produces(MediaType.TEXT_PLAIN)
    fun getWithRunBlockingAndDeferred(): String? = runBlocking {

        val jobDeferred: Deferred<String?> = async{
            demoService.fetchUrl()
        }

        jobDeferred.await()

        return@runBlocking jobDeferred.await()
    }

    //APPROACH 2:
    //EndPoint method with runBlocking CoroutineScope
    //Using return@runBlocking
    @Get("/test2")
    @Produces(MediaType.TEXT_PLAIN)
    fun getWithReturnRunBlocking(): String? = runBlocking {

        return@runBlocking demoService.fetchUrl()

    }

    //APPROACH 3:
    //EndPoint method with suspend modifier calling a suspend modifier function
    //No runBlocking neither CoroutineScope at all
    @Get("/test3")
    @Produces(MediaType.TEXT_PLAIN)
    suspend fun getSuspendFunction(): String? {

        return demoService.fetchUrlWithoutCoroutineScope()
    }
}

用於調用另一個 Rest 端點的服務

package com.tolearn.service

import kotlinx.coroutines.coroutineScope
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.Duration
import javax.inject.Singleton

@Singleton
class DemoService {

    suspend fun fetchUrl(): String? = coroutineScope {

        val client: HttpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
                .followRedirects(HttpClient.Redirect.NEVER)
                .connectTimeout(Duration.ofSeconds(20))
                .build()

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

        val response = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        print(response.get().body())

        return@coroutineScope response.get().body()
    }

    suspend fun fetchUrlWithoutCoroutineScope(): String? {

        val client: HttpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
                .followRedirects(HttpClient.Redirect.NEVER)
                .connectTimeout(Duration.ofSeconds(20))
                .build()

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

        val response = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        return response.get().body()
    }

}

如果它很重要,這里是 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"
}

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

repositories {
    mavenCentral()
    jcenter()
}

micronaut {
    runtime("netty")
    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("javax.annotation:javax.annotation-api")
    implementation("io.micronaut:micronaut-http-client")

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

    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin")
}


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

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

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


}

您通常希望避免在“主要”入口點 function 和測試之外的生產代碼中使用runBlocking 這在runBlocking 文檔中有說明。

對於協程,了解阻塞掛起之間的區別很重要。

阻塞

阻塞代碼阻止整個線程繼續。 請記住,協程用於異步編程,而不是多線程。 因此,假設兩個或多個協程可以在同一個線程上運行。 現在,當你阻塞線程時會發生什么? 沒有一個能跑。

這是危險的。 阻塞代碼絕對會毀掉你的異步編程。 有時您必須使用阻塞代碼,例如在處理文件時。 Kotlin 有特殊的方法來處理它,例如IO Dispatcher ,它將在自己的隔離線程上運行代碼,這樣它就不會破壞您的其他協程。

暫停

這是協程的核心。 這個想法是,當您的協程暫停時,它會告訴協程 scope 另一個協程可以同時執行。 暫停部分完全抽象為異步機制的工作方式。 這部分取決於 scope 和調度程序的實現。

暫停不會自動發生。 一些框架,如KTor ,在其 API 中使用協程,因此您經常會發現函數處於掛起狀態。

如果您有長期運行的操作,這些操作本質上不是暫停,您可以使用我在“阻塞”部分中提到的方法來轉換它們。

那么有什么更好的呢?

好吧,這取決於這一行:

demoService.fetchUrl()

fetchUrl()是暫停還是阻塞? 如果它被阻止,那么您的所有提案都大致相同(並且不推薦)。 如果它正在暫停,那么您的第三個選項是最好的。

如果它是阻塞的,那么處理它的最佳方法是創建一個協程 scope 並將其包裝在使其暫停的東西中,例如withContext ,然后從暫停的 function 中返回它。

但是,只有在協程中調用這些函數時才會出現這種情況。 我對 Micronaut 不熟悉。 如果該框架自動調用您的方法而不使用協程,那么在此 class 中引入它們根本沒有意義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM