零停机部署 Swift 服务:如何无中断更新

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

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

开始课程

为什么零停机部署对 Swift 开发者来说不是奢侈品,而是必需品

\n

想象一下:您的基于 Vapor 的 Swift 服务每秒处理数千个请求。您发布了一个新功能——然后用户看到了 502 Bad Gateway,日志中则是 SIGTERM 和中断的连接。熟悉吗?如果您使用 Swift 编写后端,迟早会遇到一个问题:如何更新服务而不丢失任何一个请求。

\n

零停机部署不是魔法,而是一套经过验证的技术,可以让您用新的 Swift 二进制文件替换正在运行的版本,而不会中断任何活动连接。在本文中,我将展示如何配置优雅关闭、组织进程热替换,以及让 Swift 服务与 Nginx 协同工作。所有内容都配有真实的 Swift 代码,绝不注水。

\n\n

什么是零停机,以及为什么它对 Swift 项目如此重要

\n

零停机部署是指您在更新应用程序时,用户不会察觉到哪怕一毫秒的中断。对于 Swift 服务来说,这一点尤其重要,因为:

\n
    \n
  • 客户无法容忍错误——中断的请求意味着数据或金钱的损失。
  • \n
  • Swift 服务器通常在高负载下运行(金融科技、聊天、实时 API)。
  • \n
  • 重新启动二进制文件可能需要几秒钟——而对于活动连接来说,这就像永恒一样漫长。
  • \n
\n

经典的方法——杀死旧进程并启动新进程——是行不通的。基于 Vapor 或 Hummingbird 的 Swift 应用程序必须优雅地完成当前请求的处理,然后将位置让给新实例。这正是接下来所有代码要解决的问题。

\n\n

步骤 1. Swift 中的优雅关闭:编写正确的终止逻辑

\n

零停机的第一块基石是优雅关闭。您的 Swift 服务必须能够捕获 SIGTERM 信号,并且不会在半路中断连接。Vapor 4+ 已经内置了此功能,但让我们检查一下,并在需要时添加自定义逻辑。

\n\n

Vapor 优雅关闭的基本配置

\n
import 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:

\n\n
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\n

在控制台中验证

\n

启动服务器并向其发送信号:

\n
kill -SIGTERM <PID>\n
\n

在日志中,您将看到一条关于优雅关闭的消息。如果一切正常,请继续下一步。

\n\n

步骤 2. Swift 的蓝绿部署:两个二进制文件,零停机

\n

优雅关闭解决了连接中断的问题,但没有解决更新时间的问题。当旧进程正在关闭时,新进程必须已经准备好接收请求。理想的方案是蓝绿部署。

\n

原理很简单:您有两个 Swift 服务实例(蓝色和绿色)。一个处于活动状态,另一个处于待命状态。当您更新代码时,您需要:

\n
    \n

博客

书籍推荐