使用 Kotlin 的 Canary 部署:无忧无虑的无缝发布

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么普通部署总像一场赌博?

\n

每个开发者至少都经历过一次这样的关键时刻:你按下“部署”按钮,心都提到了嗓子眼。如果新版本搞垮了数据库怎么办?如果用户看到的是 500 错误而不是主页怎么办?

\n

普通部署就像没有备用伞的跳伞。要么一切完美运行,要么你整晚盯着日志,试图弄清楚哪里出了问题。但有一种方法可以让这个过程变得安全——Canary 部署(金丝雀发布)。

\n

在本文中,我们将探讨如何使用 Ktor 和一个简单的负载均衡器在 Kotlin 中实现 Canary 部署。你将学会如何先将新代码提供给 5% 的用户,然后逐步增加流量。没错,我们将编写真正可用的代码。

\n\n

什么是 Canary 部署,为什么你需要它?

\n

想象你是一艘船的船长。与其直接驶过暗礁,不如先派一艘小船(金丝雀)探路。如果它通过了,整个船队再前进。如果它沉了,你只损失了一艘船。

\n

在 IT 世界中,这看起来是这样的:你首先为一小部分用户部署新版本的服务。如果指标正常,就增加流量。如果不正常,你可以在几秒钟内回滚。

\n

优势显而易见:

\n
    \n
  • 风险最小化 —— 错误只影响 1-5% 的用户。
  • \n
  • 快速反馈 —— 你可以在错误演变成灾难之前发现它们。
  • \n
  • A/B 测试 —— 可以比较新旧版本的性能。
  • \n
  • 零停机 —— 用户不会察觉到切换。
  • \n
\n

但美中不足的是:你需要能够管理流量、监控指标并自动化流程。Kotlin 凭借其简洁性和协程,非常适合这项任务。

\n\n

Kotlin 上的 Canary 部署架构

\n

让我们构建一个简单但实用的架构。我们将拥有:

\n
    \n
  • 负载均衡器 —— 决定多少百分比的请求发送到旧版本(stable),多少发送到新版本(canary)。
  • \n
  • 两个服务实例 —— stable 和 canary,均使用 Kotlin 和 Ktor 编写。
  • \n
  • 监控 —— 错误计数和响应时间。
  • \n
\n

负载均衡器将根据 X-Canary 头部或随机分布做出决策。我们将采用第二种方式——它更接近现实。

\n\n

编写代码:Kotlin 负载均衡器

\n

我们从负载均衡器开始。它将接收请求并将其转发到两个服务之一。为简单起见,我们将使用 Ktor 内置的 HTTP 客户端。

\n\n
import io.ktor.client.*\nimport io.ktor.client.engine.cio.*\nimport io.ktor.client.request.*\nimport io.ktor.client.statement.*\nimport io.ktor.server.application.*\nimport io.ktor.server.engine.*\nimport io.ktor.server.netty.*\nimport io.ktor.server.response.*\nimport io.ktor.server.routing.*\nimport kotlinx.coroutines.runBlocking\nimport kotlin.random.Random\n\n// 配置\nconst val STABLE_URL = "http://localhost:8081"\nconst val CANARY_URL = "http://localhost:8082"\nvar canaryPercent = 5 // 从 5% 的流量开始\n\nfun main() {\n    val client = HttpClient(CIO)\n\n    embeddedServer(Netty, port = 8080) {\n        routing {\n            get("/api/data") {\n                val useCanary = Random.nextInt(100) < canaryPercent\n                val targetUrl = if (useCanary) CANARY_URL else STABLE_URL\n\n                val response = runBlocking {\n                    client.get("$targetUrl/api/data")\n                }\n\n                call.respondText(\n                    response.bodyAsText(),\n                    contentType = response.contentType()\n                )\n            }\n\n            // 用于更改金丝雀百分比的端点\n            get("/canary/set/{perc")                

博客

书籍推荐