如何在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
相关产品推荐
相关产品推荐

