为什么零停机部署对 Swift 开发者来说不是奢侈品,而是必需品
\n想象一下:您的基于 Vapor 的 Swift 服务每秒处理数千个请求。您发布了一个新功能——然后用户看到了 502 Bad Gateway,日志中则是 SIGTERM 和中断的连接。熟悉吗?如果您使用 Swift 编写后端,迟早会遇到一个问题:如何更新服务而不丢失任何一个请求。
零停机部署不是魔法,而是一套经过验证的技术,可以让您用新的 Swift 二进制文件替换正在运行的版本,而不会中断任何活动连接。在本文中,我将展示如何配置优雅关闭、组织进程热替换,以及让 Swift 服务与 Nginx 协同工作。所有内容都配有真实的 Swift 代码,绝不注水。
\n\n什么是零停机,以及为什么它对 Swift 项目如此重要
\n零停机部署是指您在更新应用程序时,用户不会察觉到哪怕一毫秒的中断。对于 Swift 服务来说,这一点尤其重要,因为:
\n- \n
- 客户无法容忍错误——中断的请求意味着数据或金钱的损失。 \n
- Swift 服务器通常在高负载下运行(金融科技、聊天、实时 API)。 \n
- 重新启动二进制文件可能需要几秒钟——而对于活动连接来说,这就像永恒一样漫长。 \n
经典的方法——杀死旧进程并启动新进程——是行不通的。基于 Vapor 或 Hummingbird 的 Swift 应用程序必须优雅地完成当前请求的处理,然后将位置让给新实例。这正是接下来所有代码要解决的问题。
\n\n步骤 1. Swift 中的优雅关闭:编写正确的终止逻辑
\n零停机的第一块基石是优雅关闭。您的 Swift 服务必须能够捕获 SIGTERM 信号,并且不会在半路中断连接。Vapor 4+ 已经内置了此功能,但让我们检查一下,并在需要时添加自定义逻辑。
Vapor 优雅关闭的基本配置
\nimport Vapor\n\nlet app = Application()\n\n// 注册信号处理器\napp.lifecycle.use(ShutdownSignalHandler())\n\n// 标准 Vapor 代码\napp.get("health") { req in\n return "OK"\n}\n\ntry app.run()\n\n但是,如果您需要等待长时间运行的任务完成呢?例如,文件处理或发送电子邮件。让我们添加一个自定义的 ShutdownSignalHandler:
import Vapor\nimport NIOCore\n\nfinal class ShutdownSignalHandler: LifecycleHandler {\n private var runningTasks: [EventLoopFuture<Void>] = []\n \n func willBoot(_ app: Application) throws {\n // 订阅 SIGTERM 信号\n app.running?.onStop = {\n print("⏳ 收到停止信号。正在完成活动任务...")\n return self.runningTasks.flatten(on: app.eventLoopGroup.next())\n }\n }\n \n func registerTask(_ task: EventLoopFuture<Void>) {\n runningTasks.append(task)\n }\n}\n\n现在,当收到 SIGTERM 信号时,服务器将等待所有已注册的任务完成。这对于 Swift 服务的零停机部署至关重要:旧进程不会杀死它已经开始处理的请求。
在控制台中验证
\n启动服务器并向其发送信号:
\nkill -SIGTERM <PID>\n\n在日志中,您将看到一条关于优雅关闭的消息。如果一切正常,请继续下一步。
\n\n步骤 2. Swift 的蓝绿部署:两个二进制文件,零停机
\n优雅关闭解决了连接中断的问题,但没有解决更新时间的问题。当旧进程正在关闭时,新进程必须已经准备好接收请求。理想的方案是蓝绿部署。
\n原理很简单:您有两个 Swift 服务实例(蓝色和绿色)。一个处于活动状态,另一个处于待命状态。当您更新代码时,您需要:
\n- \n