如何在Ktor中获取用户通过Clikt传入的自定义配置变量
问题:Ktor中如何让后端服务获取Clikt传入的参数
我用Ktor做Web服务器,用户通过Kotlin Clikt命令行启动服务器时传入一个dbPath参数,后端处理部分REST请求时需要用到这个参数。目前Clikt类能正常接收参数并启动嵌入式服务器,但后端始终拿不到这个参数值,tiledbURI一直为null。
Clikt启动代码
class StartServer : CliktCommand(help = "Starts PHGv2 BrAPI Server") { private val myLogger = LogManager.getLogger(StartServer::class.java) val dbPath by option(help = "Full path to folder where TileDB datasets are stored. ") .default("") .validate { require(it.isNotBlank()) { "--db-path must not be blank" } } override fun run() { setupDebugLogging() // Verify the uri is valid. We only care about the hvcf dataset, // so check that one explicitly val hvcfExists = verifyURI(dbPath,"hvcf_dataset") if (!hvcfExists) { myLogger.error("hvcf_dataset does not exist in $dbPath. Exiting.") return } // Create an Args list to pass to the server // This tells the endpoint code where the datasets are located. val dbUri = "-P:TILEDB_URI=${dbPath}" val args = arrayOf(dbUri) // commandLineEnvironment reads the application.config file embeddedServer(Netty, commandLineEnvironment(args)).start(wait = true) } }
后端获取配置的代码
private val config = HoconApplicationConfig(ConfigFactory.load()) //val tiledbURI = environment.config.property("TILEDB_URI").getString() val tiledbURI = config.property("TILEDB_URI").getString() object SamplesService { private val myLogger = LogManager.getLogger(SamplesService::class.java) // Cached map of all taxa. Key is genoid mapped to Sample object private val taxa: Map<String, Sample> by lazy { taxaMap("${tiledbURI}/hvcf_dataset") } // ...其他代码 }
导入语句
import com.typesafe.config.ConfigFactory import io.ktor.server.config.*
Hocon配置文件内容
callsPageSize=10 variantsPageSize=100 # For connecting to tiledb from ktor. Users should un-comment # and edit the TILEDB_URI variable to point to their tiledb folder # When running junit tests, replace ""/Users/lcj34" in the example below # with the path to your home directory. The path should end with a / # For other use cases, replace the path with the path to the tiledb folder #TILEDB_URI="/Users/lcj34/temp/phgv2Tests/tempDir/testTileDBURI/" # Server metadata params You will need to fill these out to match your setup contactEmail = "someone@cornell.edu" documentationURL = "https://github.com/maize-genetics/phg_v2" location = "Ithaca NY" organizationName = "Institute for Genetic Diversity at Cornell University" organizationURL = "https://www.maizegenetics.net/" serverDescription = "Server to connect to the Maize PHG Tiledb through BrAPI calls." serverName = "Maize PHGv2" ktor { deployment { port = 8080 watch = [ build ] } application { modules = [ net.maizegenetics.phgv2.brapi.ApplicationKt.module ] } }
注:配置文件中的TILEDB_URI处于注释状态,因为用户无法修改fat jar里的配置文件,仅用于开发者JUnit测试。
问题分析
- 直接用
ConfigFactory.load()无法获取命令行参数:ConfigFactory.load()只会加载默认的配置文件(如application.conf),不会合并Clikt传入的-P:TILEDB_URI参数,因此tiledbURI始终为null。 environment.config的访问范围问题:environment是Ktor应用模块函数的专属参数,只能在module函数内部访问,无法在顶层代码或object中直接调用,所以直接写environment.config会编译报错。
解决方案
方案一:通过Ktor模块传递配置(推荐)
利用Ktor的ApplicationEnvironment已经合并了命令行参数的特性,在模块中获取配置后传递给服务类:
- 修改
SamplesService,添加可初始化的tiledbURI属性:
object SamplesService { lateinit var tiledbURI: String private val myLogger = LogManager.getLogger(SamplesService::class.java) // Cached map of all taxa. Key is genoid mapped to Sample object private val taxa: Map<String, Sample> by lazy { taxaMap("${tiledbURI}/hvcf_dataset") } // ...其他代码 }
- 在Ktor的
module函数中,从environment.config获取参数并初始化服务类:
fun Application.module() { // 从合并后的配置中获取TILEDB_URI val tiledbURI = environment.config.property("TILEDB_URI").getString() // 初始化SamplesService的参数 SamplesService.tiledbURI = tiledbURI // ...其他模块配置(如路由、插件等) }
方案二:通过系统属性传递参数
在Clikt启动时将参数设置为系统属性,后端通过系统属性加载:
- 修改
StartServer的run方法,添加系统属性设置:
override fun run() { setupDebugLogging() val hvcfExists = verifyURI(dbPath,"hvcf_dataset") if (!hvcfExists) { myLogger.error("hvcf_dataset does not exist in $dbPath. Exiting.") return } // 将dbPath设置为系统属性 System.setProperty("TILEDB_URI", dbPath) val dbUri = "-P:TILEDB_URI=${dbPath}" val args = arrayOf(dbUri) embeddedServer(Netty, commandLineEnvironment(args)).start(wait = true) }
- 修改后端配置加载逻辑,优先读取系统属性:
// 先加载系统属性,再回退到配置文件 private val config = HoconApplicationConfig( ConfigFactory.systemProperties().withFallback(ConfigFactory.load()) ) val tiledbURI = config.property("TILEDB_URI").getString()
内容的提问来源于stack exchange,提问作者lynnj
相关产品推荐
相关产品推荐

