1.Ktor启动
2026/1/31大约 3 分钟
1.Ktor启动
再 ktor 的启动中,启动函数是这么写的?
fun main(args: Array<String>): Unit =
io.ktor.server.netty.EngineMain.main(args)即启动函数实际委托执行 EngineMain的main函数。
另外,还需要注意的是:配置文件中的写法:
ktor {
deployment {
port = 28080
port = ${?PORT}
}
application {
modules = [ io.ktor.samples.H2ApplicationKt.module ]
}
}不知道具体实现,但是会回调 这个地方的代码:
函数的全路径: modules = [ io.ktor.samples.H2ApplicationKt.module ]
2. 函数的启动
@JvmStatic
public fun main(args: Array<String>) {
val server = createServer(args)
server.start(true)
}- 创建 server
- 启动 server
3. 创建server
io.ktor.server.netty.EngineMain#createServer
public fun createServer(
args: Array<String>
): EmbeddedServer<NettyApplicationEngine, NettyApplicationEngine.Configuration> {
val config = CommandLineConfig(args)
return EmbeddedServer(config.rootConfig, Netty) {
takeFrom(config.engineConfig)
loadConfiguration(config.rootConfig.environment.config)
}
}- typealias 类型别名
- suspend Application.() -> Unit 带接收者的 suspend lambda
- actual Kotlin 多平台实现机制
- actual constructor 主构造器
kt 高阶用法。需要研究明白这些语法。
EmbeddedServer(config.rootConfig, Netty)
创建一个netty server,前面是 root config, 中间是服务器类型,最后一个参数是加载参数:
internal fun NettyApplicationEngine.Configuration.loadConfiguration(config: ApplicationConfig) {
val deploymentConfig = config.config("ktor.deployment")
loadCommonConfiguration(deploymentConfig)
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
}
deploymentConfig.propertyOrNull("enableHttp2")?.getString()?.toBoolean()?.let {
enableHttp2 = it
}
deploymentConfig.propertyOrNull("enableH2c")?.getString()?.toBoolean()?.let {
enableH2c = it
}
}这个函数会从 解析 application.conf中关于 ktor的配置。并加载。
4. 启动 server
server.start(true)io.ktor.server.engine.EmbeddedServer#start
public actual fun start(wait: Boolean): EmbeddedServer<TEngine, TConfiguration> {
// 添加钩子函数
addShutdownHook { stop() }
// 读写锁 获取写锁
applicationInstanceLock.write {
val (application, classLoader) = try {
// 创建application
createApplication()
} catch (cause: Throwable) {
// 销毁 application
destroyApplication()
if (watchPatterns.isNotEmpty()) {
cleanupWatcher()
}
throw cause
}
applicationInstance = application
applicationClassLoader = classLoader
}
// 第三步:异步打印服务器监听地址
CoroutineScope(application.coroutineContext).launch {
engine.resolvedConnectors().forEach {
val host = escapeHostname(it.host)
environment.log.info(
"Responding at ${it.type.name.lowercase()}://$host:${it.port}"
)
}
}
engine.start(wait)
return this
}- applicationInstanceLock.write
- 使用 ReentrantReadWriteLock 的写锁,确保多线程环境下安全地初始化/重建应用实例(尤其在开发模式下热重载时)。
- createApplication()
- 创建 Application 对象(Ktor 应用的核心上下文)。
- 在开发模式下,还会设置自定义类加载器(OverridingClassLoader),用于监听代码变更并热重载。
- 异常处理
- 如果创建失败(如模块加载错误),先清理已分配的资源(destroyApplication()、cleanupWatcher()),再抛出异常
- 避免“半启动”状态导致资源泄漏。
- 保存实例
- 将新创建的 application 和 classLoader 存入成员变量,供后续请求使用。
第三步:异步打印服务器监听地址
- 为什么用 launch?
- engine.start(wait) 可能会阻塞(当 wait=true),但我们希望尽快打印日志,所以用协程异步执行。
- 即使主线程被 engine.start(true) 阻塞,日志也能及时输出。
- resolvedConnectors()
- 获取服务器实际绑定的协议(HTTP/HTTPS)、主机和端口。
- 例如:[Connector(HTTP, "0.0.0.0", 8080)]
第四步:启动底层引擎
第四步:启动底层引擎- 调用具体引擎(如 Netty、Tomcat)的 start 方法。
- 行为取决于 wait 参数:
- wait = true:阻塞当前线程,直到服务器停止(通常用于主函数)。
- wait = false:启动后立即返回,服务器在后台线程运行。
🧠 整体流程总结
| 步骤 | 动作 | 目的 |
|---|---|---|
| 1 | 注册关闭钩子 | 确保 JVM 退出时优雅关闭服务器 |
| 2 | 加锁创建 Application | 安全初始化应用上下文(支持热重载) |
| 3 | 异步打印监听地址 | 快速反馈服务器启动信息 |
| 4 | 启动底层引擎 | 真正开始监听 HTTP 请求 |
| 5 | 返回 this | 支持链式调用 |