You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Vapor 3中保持WebSocket连接存活?

在Vapor 3中保持WebSocket连接存活(适配Heroku环境)

嘿,我太懂你在Heroku上遇到的WebSocket断开问题了——Heroku的路由器会主动切断超过55秒没活动的闲置连接,所以心跳ping肯定是刚需。你之前在Vapor 2里用的后台循环思路完全没问题,但Vapor 3基于SwiftNIO重构了异步API,旧代码没法直接复用,我来给你改成适配Vapor 3的写法:

核心逻辑变化说明

Vapor 2里的background闭包和console.wait都是阻塞式的,在Vapor 3的非阻塞异步模型里会直接卡死事件循环。所以我们得换成SwiftNIO提供的重复定时任务来实现心跳,全程保持异步非阻塞。

Vapor 3完整实现代码

你可以把这段逻辑直接嵌入到你的WebSocket处理路由里:

func webSocketHandler(_ req: Request, ws: WebSocket) {
    // 保存定时任务引用,方便连接关闭时取消
    var pingTask: RepeatedTask?
    
    // 可选:监听客户端返回的pong,直观确认连接状态
    ws.onPong { [weak ws] in
        print("收到客户端pong响应,连接正常")
    }
    
    // 创建每隔10秒发送一次ping的重复任务
    pingTask = req.eventLoop.scheduleRepeatedTask(
        initialDelay: .seconds(0),  // 首次发送无延迟
        delay: .seconds(10)         // 后续每10秒触发一次
    ) { task in
        // 先检查WebSocket是否还处于打开状态
        guard let ws = ws, ws.state == .open else {
            // 连接已关闭,立即取消任务
            task.cancel()
            return
        }
        
        // 发送ping,并处理可能的发送错误
        ws.ping { error in
            if let error = error {
                print("发送ping失败:\(error.localizedDescription)")
                task.cancel()
            }
        }
    }
    
    // 当WebSocket连接关闭时,取消ping任务,避免内存泄漏
    ws.onClose.whenComplete { _ in
        pingTask?.cancel()
        print("WebSocket连接关闭,已取消ping任务")
    }
    
    // 这里添加你的其他WebSocket业务逻辑
    ws.onText { ws, text in
        ws.send("收到消息:\(text)")
    }
}

关键注意事项

  • 绝对不能阻塞事件循环:千万别用console.wait这类阻塞方法,必须用scheduleRepeatedTask这种异步定时API,否则会拖垮整个应用的性能。
  • 及时取消任务:WebSocket关闭时一定要取消ping任务,防止内存泄漏。
  • 适配Heroku超时规则:Heroku的闲置连接超时是55秒,10秒的ping间隔完全在安全范围内,能确保连接不会被路由器主动切断。
  • 避免循环引用:闭包里用[weak ws],防止WebSocket和任务互相持有导致内存泄漏。

这样调整后,你的WebSocket连接就能在Heroku上稳定保持存活了。

内容的提问来源于stack exchange,提问作者Nevin Jethmalani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:55:01