Ktor — 用 Kotlin DSL 拼出来的异步 Web 框架
是什么
Ktor 是 JetBrains 给 Kotlin 出的一套异步 Web 框架,server 和 client 都管。日常类比:像乐高——只给你”地板(HTTP 引擎)“和一堆零件(认证、JSON、压缩),你自己挑要装哪些、按什么顺序装。
它不像 Spring Boot 那样”一开箱什么都给你配好”——Spring 是带说明书的成品玩具屋;Ktor 是一袋散件。新手会觉得”怎么连 JSON 解析都要我自己 install”,但反过来你也不会被它绑死在某种日志或 ORM 上。
最小启动代码长这样:
fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respondText("Hello, Ktor") } } }.start(wait = true)}这段代码做了三件事:选一个 HTTP 引擎(Netty)、起一个端口、声明一条路由。全是 Kotlin 函数调用,没有注解,没有 XML,没有 application.properties。
为什么重要
不理解 Ktor,下面这些事都没法解释:
- 为什么 Kotlin 后端社区不直接用 Spring Boot——因为 Spring 是给 Java 的注解世界设计的,Kotlin 用着别扭
- 为什么 Android 应用越来越多用 Ktor 而不是 Retrofit/OkHttp——因为 Ktor client 是 Kotlin Multiplatform,iOS 能复用同一份代码
- 为什么”unopinionated”是把双刃剑——给你自由也意味着没人替你做选型决定
- 为什么 install(X) {…} 这种写法这么火——它是 DSL + 插件系统的教科书例子
核心要点
Ktor 的设计可以拆成 三个支柱:
-
Engine 抽象:HTTP 接收/发送由独立 engine 处理(Netty / Jetty / CIO / Tomcat)。类比:你写菜单,但厨房用煤气还是电磁炉随便换。生产推荐 Netty,纯 Kotlin 玩具项目可以用 CIO。
-
Plugin(旧名 Feature)+ Pipeline:每个能力都是 plugin,通过
install(X) {...}装到 pipeline 上。Pipeline 是一条流水线,请求从一头进来,依次过 plugin(认证 → 日志 → 路由 → 序列化 → 响应)。装的顺序 = 拦截的顺序。 -
Coroutine 原生:每个 handler 都是
suspend函数。你写delay(100)或httpClient.get(...)不会阻塞线程——线程被让给别的请求用,等 IO 回来再继续。这点和 Express、Spring MVC(非 WebFlux)有本质差别。
三件事合起来:DSL 装配 + 插件流水线 + 协程并发。
实践案例
案例 1:带 JWT 鉴权的 REST API
install(Authentication) { jwt("auth-jwt") { verifier(JWT.require(Algorithm.HMAC256("secret")).build()) validate { cred -> if (cred.payload.getClaim("uid").asString() != null) JWTPrincipal(cred.payload) else null } }}
routing { authenticate("auth-jwt") { get("/me") { val uid = call.principal<JWTPrincipal>()!!.payload.getClaim("uid").asString() call.respondText("hello, user $uid") } }}install 把 JWT 校验装到 pipeline,authenticate { ... } 是一个作用域块——块内的路由都会走这个校验,块外不走。
案例 2:WebSocket 实时推送
install(WebSockets)
routing { webSocket("/ws") { for (frame in incoming) { if (frame is Frame.Text) outgoing.send(Frame.Text("echo: ${frame.readText()}")) } }}incoming 和 outgoing 都是 Kotlin Channel——你 for 循环消费就行,底层异步 IO 由协程接管。换 Express 写 WebSocket,得自己 on(‘message’)、on(‘close’)、自己管状态。
案例 3:Multiplatform HTTP client
val client = HttpClient(CIO) { install(ContentNegotiation) { json() } }val users: List<User> = client.get("https://api.example.com/users").body()这段代码 同一份可以编译进 Android、iOS、JVM 后端、JS 浏览器——这是 Ktor 相对 Retrofit 的最大杀手锏。client 拆掉 server 也能单独用,跨端项目几乎是默认选择。
踩过的坑
-
install 顺序就是拦截顺序:把
Authentication装在CallLogging后面,日志会先出现再被 401 拒绝;反过来才是常规打法。新手装错了会发现”日志全打出来了但接口仍然被拒”很难定位。 -
CIO 引擎在生产被打爆:CIO 是纯 Kotlin 实现,写 demo 很快,但成熟度不如 Netty。压测一上量经常出现 socket close 异常。生产默认 Netty。
-
HOCON 配置 vs 代码配置覆盖:
application.conf写了port = 8080,代码里embeddedServer(Netty, port = 9090)也起得来,最终监听 9090——配置文件被静默忽略,团队协作时极难排查。统一一种风格。 -
ContentNegotiation 没装就报奇怪错:忘了
install(ContentNegotiation) { json() }的话,call.receive<MyDto>()会抛Cannot transform this request's content而不是友好的”你忘了装 JSON 解析器”。
适用 vs 不适用场景
适用:
- Kotlin 后端项目,特别是中小型微服务和 BFF(Backend for Frontend)
- 跨端项目(Android/iOS/Web 共用一套 client)
- 团队有 Kotlin 经验、想要 DSL 风格、不想被 Spring 注解绑死
- 实时类应用:WebSocket、SSE、长连接
不适用:
- Java 主导的老项目——Ktor 在 Java 里写出来很难看,Spring Boot 仍是更顺手的选择
- 需要”开箱即有的”完整生态(事务、ORM、安全、批处理)→ 选 Spring Boot 或 Quarkus
- 团队全是 Spring 经验、不想学 DSL 思维 → 强行上 Ktor 会卡在选型上
- 需要稳定的 servlet 生态兼容(老 J2EE 项目)→ 用传统 servlet 容器
历史小故事(可跳过)
- 2018 年:JetBrains 把 Ktor 1.0 正式开源,灵感来自之前几个 Kotlin 社区的废弃 web 框架(Wasabi、Kara),目标是给 Kotlin 一个”原生味道”的后端框架
- 2020 年:1.x 系列稳定下来,Plugin 还叫 Feature,社区开始有人在生产用
- 2022 年:2.0 大版本,client 被重写、API 整理、命名从 Feature 改成 Plugin
- 2024 年:3.0 进一步整理 plugin API,去掉一些过渡期遗留
- 2026 年:当前 stable 3.5.0,13k+ stars,仍由 JetBrains 官方维护
40 年后回头看后端框架的发展,从 Servlet → Spring → Spring Boot → Ktor,每一代都是”减少样板代码”的努力。Ktor 是 Kotlin 时代这条路线的代表。
学到什么
- DSL + 插件 + 协程是 Ktor 的三件套,理解了就理解了 Kotlin 后端这一代设计哲学
- Unopinionated 不等于”什么都没”,它等于”什么都能换”——代价是你得自己做选型
- Pipeline 顺序 = install 顺序,这是 Ktor 模型里最反直觉但最关键的一点
- Multiplatform client 是真正的差异化,跨端项目几乎是默认答案
延伸阅读
- 官方文档:ktor.io(教程清晰,但深度内容要翻 issues)
- 视频:Hadi Hariri — Ktor: Kotlin Web Framework(JetBrains 官方布道,1 小时入门)
- spring-boot —— 对照阵营:注解驱动 vs DSL 驱动
- vertx —— 同样异步、跨语言,但是 Java 优先
- micronaut —— Java 系的”轻量 Spring”,编译期 DI 是它的差异点
关联
- spring-boot —— Java/Kotlin 后端的另一极:开箱即用 vs 你自己拼
- express —— Node 世界类似的”轻量 + 中间件”模型,但没协程
- fastapi —— Python 类似定位,靠类型注解而非 DSL
- vertx —— JVM 异步老兵,事件循环模型 vs Ktor 协程模型
- micronaut —— Kotlin/Java 都能写,编译期 DI vs Ktor 运行期装配
- axum —— Rust 里类似的”组合式中间件”思路
- koa —— Node 里的洋葱式 middleware,思路同源
反向链接
- aspnetcore —— ASP.NET Core — 微软跨平台 web 框架
- axum —— axum — 用 Rust 类型系统当『路由参数表』的 Web 框架
- dropwizard —— Dropwizard — Java 微服务的”开箱即用 12-factor 起步包”
- express —— Express — Node.js 最经典的 Web 框架
- fastapi —— FastAPI — 用 Python 类型注解写 API
- hanami —— Hanami — Ruby 里既不是 Rails 也不是 Sinatra 的第三选择
- koa —— Koa — async/await + ctx 对象 + 洋葱模型 的极简 Node.js web 框架
- micronaut —— Micronaut — 编译期搞定 DI 的 JVM 云原生框架
- sinatra —— Sinatra — 用 Ruby 三行代码起一个 web 服务
- spring-boot —— Spring Boot — 用 Auto-configuration 把 Java 后端从 XML 地狱里救出来的事实标准框架
- vertx —— Vert.x — Eclipse 出品的 polyglot reactive JVM toolkit,用事件总线 + verticle 把 Node.js 那套搬到多语言