kotlin - 如何确保清除由ktor Websocket客户端创建的所有Kotlin协程?

标签 kotlin kotlin-coroutines ktor

我正在努力围绕Kotlin协程和Ktors websocket支持。我的理解是runBlocking将创建一个作用域,并且只要该范围内有协程存在(或子范围),它将被阻塞,但是我在下面的测试中对runBlocking的调用返回时仍然有两个协程存在。

为什么我在这里泄漏协程?

package dummy

import io.ktor.client.HttpClient
import io.ktor.client.features.websocket.WebSockets
import io.ktor.client.features.websocket.wss
import io.ktor.http.HttpMethod
import io.ktor.http.cio.websocket.Frame
import io.ktor.http.cio.websocket.readBytes
import io.ktor.http.cio.websocket.readText
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.*
import kotlinx.coroutines.debug.DebugProbes
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

@ExperimentalCoroutinesApi
@KtorExperimentalAPI
class WebsocketTest {

    @Test
    fun tidy() {
        DebugProbes.install()

        runBlocking {
            val socketJob = Job()

            launch(CoroutineName("Websocket") + socketJob) {
                println("Connecting to websocket")
                connectWebsocket(socketJob)
                println("Websocket dead?")
            }

            launch(CoroutineName("Ninja socket killer")) {
                delay(3500)
                println("Killing websocket client")
                socketJob.cancel(message = "Time to die..")
            }
        }

        println("\n\n-------")
        DebugProbes.dumpCoroutines(System.err)
        Assertions.assertEquals(0, DebugProbes.dumpCoroutinesInfo().size, "It would be nice if all coroutines had been cleared up by now..")
    }

}


@KtorExperimentalAPI
private suspend fun connectWebsocket(socketJob: CompletableJob) {

    val client = HttpClient {
        install(WebSockets)
    }

    socketJob.invokeOnCompletion {
        println("Shutting down ktor http client")
        client.close()
    }

    client.wss(
            method = HttpMethod.Get,
            host = "echo.websocket.org",
            port = 443,
            path = "/"
    ) {

        send(Frame.Text("Hello World"))

        for (frame in incoming) {
            when (frame) {
                is Frame.Text -> println(frame.readText())
                is Frame.Binary -> println(frame.readBytes())
            }

            delay(1000)
            send(Frame.Text("Hello World"))
        }
    }

}

build.gradle.kts
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
    kotlin("jvm") version "1.3.41" apply true
}

repositories {
    mavenCentral()
}

val ktorVersion = "1.2.3"
val junitVersion = "5.5.1"

dependencies {
    implementation(kotlin("stdlib-jdk8"))

    implementation("io.ktor:ktor-client-websockets:$ktorVersion")
    implementation("io.ktor:ktor-client-okhttp:$ktorVersion")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.3.0-RC2")


    testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
}

tasks.withType<Test> {
    useJUnitPlatform()
    testLogging {
        showExceptions = true
        showStackTraces = true
        exceptionFormat = TestExceptionFormat.FULL
        events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
    }
}

最佳答案

好像我已经弄清楚了(显然是在将我的头发撕开足够长的时间以使其成为第一篇文章之后)。当我写这篇文章时,我泄露了两个协程,其中一个“解决了自己”(我对此并不满意,但是无论如何我都无法复制它)。

第二个协程泄漏了,因为来自Ktor的Nonce.kt在GlobalScope中明确启动了一个协程。

https://github.com/ktorio/ktor/blob/master/ktor-utils/jvm/src/io/ktor/util/Nonce.kt#L30

private val nonceGeneratorJob =
GlobalScope.launch(
    context = Dispatchers.IO + NonCancellable + NonceGeneratorCoroutineName,
    start = CoroutineStart.LAZY
) { ....

关于kotlin - 如何确保清除由ktor Websocket客户端创建的所有Kotlin协程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57580222/

相关文章:

kotlin - 如何在 Ktor 微服务应用中安排任务

java - 如何限制 fragment 中的后退 Action

kotlin - Vertx和Kotlin协程永远挂起

kotlin - Kotlin 是否支持部分应用程序?

android - KMM : How to resolve Incompatible abi version. 当前默认为 '1.4.2' ,找到 '1.5.0' 。 1.5.20编译器产生的库?

reactjs - 如何使用 Gradle 将 Reason 和 Ktor 应用程序打包在一起?

android - 导航组件未替换当前 fragment Activity

Android:带有协程的易碎 ViewModel 单元测试

android - 从 RxJava2 迁移到 Kotlin 协程

android - 使用 Datastore 防止内存泄漏的最佳做法是什么?