7、Ktor学习-配置服务器;

2,789 阅读5分钟

  Ktor在外部配置文件中使用HOCON(Human-Optimized Config Object Notation)格式。 在此文件中,您可以配置要侦听的端口或要加载的模块等内容。 此格式类似于JSON,但经过优化,可供人类读取和编写,并支持环境变量替换等其他功能。 在这种情况下,您将服务器引擎配置为与指向特定EngineMain的mainClassName一起使用。

  当使用embeddedServer时,Ktor还使用一组带有类型DSL(域特定语言)的lambda来配置应用程序和服务器引擎。

HOCON文件


  这是配置Ktor应用程序的首选方法,因为它允许您轻松更改配置,而无需重新编译应用程序。

  当Ktor使用EngineMain启动时,或者通过调用commandLineEnvironment,它会尝试从应用程序资源加载名为application.conf的HOCON文件。 您可以使用命令行参数更改文件的位置。

可用作mainClassName的可用开发引擎:

  io.ktor.server.cio.EngineMain
  io.ktor.server.tomcat.EngineMain
  io.ktor.server.jetty.EngineMain
  io.ktor.server.netty.EngineMain

  Ktor仅要求您使用ktor.application.modules属性指定在启动服务器时要加载的模块。 所有其他属性都是可选的。

  Ktor(application.conf)的典型简单HOCON文件如下所示:

ktor {
    deployment {
        port = 8080
    }

    application {
        modules = [ io.ktor.samples.metrics.MetricsApplicationKt.main ]
    }
}

  使用.表示法相当于:

ktor.deployment.port = 8080
ktor.application.modules = [ io.ktor.samples.metrics.MetricsApplicationKt.main ]

  Ktor允许您进行更多配置:从其他核心配置到Ktor功能,甚至是应用程序的自定义配置:

ktor {
    deployment {
        environment = development
        port = 8080
        sslPort = 8443
        autoreload = true
        watch = [ httpbin ]
    }

    application {
        modules = [ io.ktor.samples.httpbin.HttpBinApplicationKt.main ]
    }

    security {
        ssl {
            keyStore = build/temporary.jks
            keyAlias = mykey
            keyStorePassword = changeit
            privateKeyPassword = changeit
        }
    }
}

jwt {
    domain = "https://jwt-provider-domain/"
    audience = "jwt-audience"
    realm = "ktor app"
}

youkube {
  session {
    cookie {
      key = 03e156f6058a13813816065
    }
  }
  upload {
    dir = ktor-samples/ktor-samples-youkube/.video
  }
}

注:你可以安装一个用于HOCON的IntelliJ插件。

命令行


  使用commandLineEnvironment(任何EngineMain main)时,可以使用多个开关和配置参数来配置应用程序模块。

  如果从命令行使用-config = anotherfile.conf启动应用程序,它将从特定的本地文件而不是从资源加载配置文件。

java -jar myapp-fatjar.jar -port=8080

配置embeddedServer


   embeddedServer是启动Ktor应用程序的简单方法。 您提供自己的主要功能,并且更明确,更容易理解究竟发生了什么。

  embeddedServer包含一个可选参数configure,允许您设置第一个参数中指定的引擎的配置。 独立于所使用的引擎,您将拥有一些可配置的属性:

embeddedServer(AnyEngine, configure = {
    // Size of the event group for accepting connections
    connectionGroupSize = parallelism / 2 + 1
    // Size of the event group for processing connections,
    // parsing messages and doing engine's internal work 
    workerGroupSize = parallelism / 2 + 1
    // Size of the event group for running application code 
    callGroupSize = parallelism 
}) {
    // ...
}.start(true)

多个连接器


  可以使用applicationEngineEnvironment通过代码定义几个连接器。

  在applicationEngineEnvironment中,您可以定义HTTP和HTTPS连接器:

  定义一个HTTP连接器:

connector {
    host = "0.0.0.0"
    port = 9090
}

  定义一个HTTPS连接器:

sslConnector(keyStore = keyStore, keyAlias = "mykey", keyStorePassword = { "changeit".toCharArray() }, privateKeyPassword = { "changeit".toCharArray() }) {
    port = 9091
    keyStorePath = keyStoreFile.absoluteFile
}

  完整的例子:

fun main(args: Array<String>) {
    val env = applicationEngineEnvironment {
        module {
            main()
        }
        // Private API
        connector {
            host = "127.0.0.1"
            port = 9090
        }
        // Public API
        connector {
            host = "0.0.0.0"
            port = 8080
        }
    }
    embeddedServer(Netty, env).start(true)
}

该应用程序将处理所有连接。 您可以访问每个ApplicationCall的本地端口,因此您可以根据本地端口决定要执行的操作:

fun Application.main() {
    routing {
        get("/") {
            if (call.request.local.port == 8080) {
                call.respondText("Connected to public api")
            } else {
                call.respondText("Connected to private api")
            }
        }
    }
}

Netty


使用Netty作为引擎时,除了常用属性外,还可以配置其他一些属性:

embeddedServer(Netty, configure = {
    // 存储无法立即处理的[ApplicationCall]实例的队列的大小
    requestQueueLimit = 16 
    shareWorkGroup = false 
    // 用户提供的功能来配置Netty的[ServerBootstrap]
    configureBootstrap = {
        // ...
    } 
    // 向客户端发送响应的超时(以秒为单位)
    responseWriteTimeoutSeconds = 10 
}) {
    // ...
}.start(true)

Jetty


使用Jetty作为引擎时,除了常用属性外,还可以配置Jetty服务器。

embeddedServer(Jetty, configure = {
    // 提供lambda的属性,该lambda将在Jetty服务器初始化期间以服务器实例作为参数进行调用。
    configureServer = {
        // ...
    } 
}) {
    // ...
}.start(true)

CIO


使用CIO(Coroutine I/O)作为引擎时,除了常用属性外,还可以配置connectionIdleTimeoutSeconds属性。

embeddedServer(CIO, configure = {
    // 服务器将保持HTTP IDLE连接打开的秒数。
    // 如果没有活动请求正在运行,则连接是IDLE。
    connectionIdleTimeoutSeconds = 45
}) {
    // ...
}.start(true)

Tomcat


使用Tomcat时,除了常用属性外,还可以配置Tomcat服务器。

embeddedServer(Tomcat, configure = {
    // 提供在将Tomcat服务器初始化期间以服务器实例作为参数调用的lambda的属性。
    configureTomcat { // this: Tomcat ->
        // ...
    }
}) {
    // ...
}.start(true)

可用的配置参数


有一个Ktor可以理解的属性列表,您可以从命令行或HOCON文件传递这些属性。

参数 参数路径 默认值 描述
jar JAR文件的路径
config 配置文件的路径(而不是资源中的application.conf)
host ktor.deployment.host 0.0.0.0 绑定主机
port ktor.deployment.port 80 绑定端口
watch ktor.deployment.watch [] 用于监视重新加载的程序包路径
ktor.application.id Application 用于记录的应用程序标识符
ktor.deployment.callGroupSize parallelism 运行应用程序代码的事件组大小
ktor.deployment.callGroupSize parallelism 运行应用程序代码的事件组大小
ktor.deployment.connectionGroupSize parallelism / 2 + 1 事件组接受连接大小

从代码中读取配置


如果您使用的是EngineMain而不是embeddedServer,则会加载HOCON文件,您可以访问其配置属性。

您还可以定义任意属性路径以配置应用程序。

val port: String = application.environment.config
    .propertyOrNull("ktor.deployment.port")?.getString()
    ?: "80"

使用环境变量


对于HOCON,如果要使用环境变量配置某些参数,可以使用${ENV}语法来使用环境替换。 例如:

ktor {
    deployment {
        port = ${PORT}
    }
}

如果由于环境不存在而要为属性提供默认值,可以使用默认值设置该属性,然后使用$ {?ENV}语法再次设置该属性:

ktor {
    deployment {
        port = 8080
        port = ${?PORT}
    }
}

如果您使用的是embeddedServer,您仍然可以使用Java中的System.getenv。 例如:

val port = System.getenv("PORT")?.toInt() ?: 8080

如何区分开发环境


您可能希望根据服务器是在本地运行还是在生产服务器上执行不同的操作。

HOCON & ENV

您可以使用application.conf文件来设置将保存环境的变量,然后在运行时检查该变量并确定要执行的操作。 您可以将其配置为检查环境变量KTOR_ENV并提供默认值dev。 然后在生产中设置KTOR_ENV = prod

ktor {
    environment = dev
    environment = ${?KTOR_ENV}
}

您可以从应用程序访问此配置,并使用一些扩展属性来简化操作:

fun Application.module() {
    when {
        isDev -> {
            // Do things only in dev   
        }
        isProd -> {
            // Do things only in prod
        }
    }
    // Do things for all the environments
}

val Application.envKind get() = environment.config.property("ktor.environment").getString()
val Application.isDev get() = envKind == "dev"
val Application.isProd get() = envKind != "dev"