kotlin 修改ktors调用协同例程上下文

falq053o  于 2023-06-24  发布在  Kotlin
关注(0)|答案(1)|浏览(116)

因此,我使用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())
        }

    }
}

我所做的是确保从父launch(标记为[1])调用的所有函数都可以访问这种“作用域”数据。
然而,我的应用程序非常大,我希望这对路由处理的所有请求都有效,而不希望每个路由都需要这种 Package 。
真正伟大的是:

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

routing {
    get("/hello") {
        val threadLocalValue = dataThreadLocal.get()
    ...

这显然不起作用,因为intercept中的协程作用域没有包含路由的协程作用域。
我相信在幕后发生的是,每个调用都是在父协同例程范围内启动的(类似于我们的“请求线程”)。有没有什么方法可以让我修改一下上下文呢?有没有一种方法可以告诉ApplicationEngine+一个额外的上下文元素,每当这个新的协同例程上下文启动时?

gmxoilav

gmxoilav1#

您可以使用修改后的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
        }
    }
}

魔法就发生在这条线上:

val applicationEnvironment = commandLineEnvironment(args) { parentCoroutineContext += EmptyCoroutineContext /* your context */ }

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

相关问题