我使用 ktor 作为网络服务器。用户调用 Kotlin Clikt 命令来启动服务器,并向其传递一个稍后在后端处理某些 REST 请求时需要的变量。
然后,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
// https://ktor.io/docs/configuration.html#hocon-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")
}
....
tiledbURI 的值始终为 null(但代码可以编译)。 如果我按照文档中的示例进行操作,它会显示从环境中获取值:
val tiledbURI = environment.config.property("TILEDB_URI").getString()
但是“环境”未知,无法编译。是否需要不同的导入?我的相关进口是:
import com.typesafe.config.ConfigFactory
import io.ktor.server.config.*
我错过了进口吗?或者这个变量只是为了启动服务器而存在,并且它们没有存储在配置文件中以供进一步访问?
更新/编辑:
hcon 配置文件是这样的:
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 = "[email protected]"
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 中。与更新配置文件中的 TILEDB_URI 变量相关的注释主要用于开发人员 junit 测试。
我们需要的是用户能够向我们传递一个我们可以为此参数设置的值。
我可以通过进行以下更改来使其正常工作。基本上,我将用户参数传递给 ktor 路由代码。上面显示的文件更改如下:
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 dbUri = "TILEDB_URI=${dbPath}"
val args = arrayOf(dbUri)
val server = embeddedServer(Netty,port=8080) {
module(args)
}
server.start(true)
}
}
然后在 Application.kt 中通过以下方式处理:
fun Application.module(args:Array<String>) {
install(DefaultHeaders)
install(CallLogging)
install(ContentNegotiation) {
json(Json {
prettyPrint = false
isLenient = true
encodeDefaults = true
})
}
// Setup routing. Individual endpoints create Kotlin Route extensions
// to handle processing REST requests.
routing {
// this method routes brapi/v2/
// Within apiRoute(), specific endpoint calls are handled
apiRoute(args)
}
}
我们继续传递这个参数,直到它到达实际处理数据的函数:
fun Route.samples(args:Array<String>) {
val samplesService = SamplesService
val tiledbURI = args[0].substringAfter("TILEDB_URI=").substringBefore(" ")
route("/samples") {
get("") {
call.respond(
SampleListResponse(
Metadata(),
SampleListResponseResult(samplesService.lcjAllTaxaNames(tiledbURI).toTypedArray())
)
)
}
}
}
如果有更好的方法来传递参数值,请告诉我,但这目前有效。