Цель: я хочу закодировать конечную точку, которая потребляет другую конечную точку, используя преимущества легких сопрограмм, предполагая, что я кодирую легкий ассинхронный клиент конечной точки.
Моя предыстория: впервые пытаюсь использовать Kotlin Coroutine. Я изучал последние дни и ищу вокруг. Я нашел множество статей, объясняющих, как использовать Coroutine в Android, и несколько других, объясняющих, как использовать Coroutine в основной функции. К сожалению, я не нашел статей, объясняющих, как кодировать конечную точку контроллера с сопрограммами, и это вызвало в моей голове сигнал, если я делаю что-то не рекомендованное.
Текущая ситуация: я успешно создал несколько подходов с использованием сопрограмм, но мне интересно, какой из них наиболее подходит для традиционного GET. Вдобавок мне интересно, как правильно работать с Exception.
Главный вопрос: какой из приведенных ниже подходов рекомендуется и о каком исключительном лечении мне следует позаботиться?
Связанный вторичный вопрос: в чем разница между
fun someMethodWithRunBlocking(): String? = runBlocking {
return@runBlocking ...
}
а также
suspend fun someMethodWithSuspendModifier(): String?{
return ...
}
Все приведенные ниже предварительные варианты работают и возвращают ответ json, но я не знаю, может ли runBlocking для метода конечной точки и возврат return @ runBlocking вызвать у меня негативный недостаток.
Контроллер (конечная точка)
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()
}
}
Служба, используемая для вызова другой конечной точки отдыха
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"
}
}
}