修改 ktors 调用协程上下文

Roh*_*bhu 6 kotlin kotlin-coroutines

因此,我正在使用ktor并希望通过ThreadLocal. 我正在看的是:

val dataThreadLocal = ThreadLocal<String>()
suspend fun fromOtherFunction() = "From other function -- ${dataThreadLocal.get()}"

routing {
    get("/hello") {
        // [1]
        launch(this.coroutineContext + dataThreadLocal.asContextElement(value = "world")) {
            val fromHere = async {
                "ThreadLocal value: ${dataThreadLocal.get()}"
            }

            val fromThere = async {
                fromOtherFunction()
            }

            call.respond(fromHere.await() + "," + fromThere.await())
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我正在做的是确保从父级launch(标记为 [1])调用的所有函数都可以访问这种“范围内”数据。

但是,我的应用程序非常大,我希望它适用于路由处理的所有请求,并且不希望每个路由都需要这种包装。

真正伟大的是:

intercept(ApplicationCallPipeline.Features) {
    launch(this.coroutineContext + dataThreadLocal.asContextElement(value = "world")) {
    ...
    }
}

routing {
    get("/hello") {
        val threadLocalValue = dataThreadLocal.get()
    ...
Run Code Online (Sandbox Code Playgroud)

这显然不起作用,因为 中的协程作用域intercept不包含路由的协程作用域。

我相信在幕后发生的是,每个调用都是在父协程范围中启动的(类似于我们如何拥有“请求线程”)。有没有办法让我修改其中的上下文?有没有一种方法可以让我在这个新的协同例程上下文启动时告诉另一个上下文元素ApplicationEngine+

Hac*_*ck5 1

您可以使用修改后的ApplicationEngineEnvironment. 要访问它,您需要编写自己的 EngineMain 或类似的类。这是 Netty 的修改版本:

/*
 * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

import io.ktor.server.config.*
import io.ktor.server.engine.*
import kotlinx.coroutines.ExperimentalCoroutinesApi

/**
 * Netty engine
 */
public object EngineMain {
    /**
     * Main function for starting EngineMain with Netty
     * Creates an embedded Netty application with an environment built from command line arguments.
     */
    @JvmStatic
    public fun main(args: Array<String>) {

        val applicationEnvironment = commandLineEnvironment(args) { parentCoroutineContext += EmptyCoroutineContext /* your context */ }
        val engine = NettyApplicationEngine(applicationEnvironment) { loadConfiguration(applicationEnvironment.config) }

        engine.start(true)
    }

    internal fun NettyApplicationEngine.Configuration.loadConfiguration(config: ApplicationConfig) {
        val deploymentConfig = config.config("ktor.deployment")
        loadCommonConfiguration(deploymentConfig)
        deploymentConfig.propertyOrNull("requestQueueLimit")?.getString()?.toInt()?.let {
            requestQueueLimit = it
        }
        deploymentConfig.propertyOrNull("runningLimit")?.getString()?.toInt()?.let {
            runningLimit = it
        }
        deploymentConfig.propertyOrNull("shareWorkGroup")?.getString()?.toBoolean()?.let {
            shareWorkGroup = it
        }
        deploymentConfig.propertyOrNull("responseWriteTimeoutSeconds")?.getString()?.toInt()?.let {
            responseWriteTimeoutSeconds = it
        }
        deploymentConfig.propertyOrNull("requestReadTimeoutSeconds")?.getString()?.toInt()?.let {
            requestReadTimeoutSeconds = it
        }
        deploymentConfig.propertyOrNull("tcpKeepAlive")?.getString()?.toBoolean()?.let {
            tcpKeepAlive = it
        }
        deploymentConfig.propertyOrNull("maxInitialLineLength")?.getString()?.toInt()?.let {
            maxInitialLineLength = it
        }
        deploymentConfig.propertyOrNull("maxHeaderSize")?.getString()?.toInt()?.let {
            maxHeaderSize = it
        }
        deploymentConfig.propertyOrNull("maxChunkSize")?.getString()?.toInt()?.let {
            maxChunkSize = it
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

魔法发生在线路上:

        val applicationEnvironment = commandLineEnvironment(args) { parentCoroutineContext += EmptyCoroutineContext /* your context */ }
Run Code Online (Sandbox Code Playgroud)

您不必有一个名为 的类EngineMain,为了简单起见,我只是复制粘贴(请参阅https://github.com/ktorio/ktor/blob/main/ktor-server/ktor-server-netty/jvm/src/ io/ktor/server/netty/EngineMain.kt)。您可以只使用 main 函数并手动进行配置。