Vapor WebSocket持续占用Heroku PostgreSQL数据库连接问题求助
Hey,我之前在Vapor + Heroku PostgreSQL的项目里也碰到过一模一样的连接泄漏问题——WebSocket关了但连接还占着,很快就把Heroku那20个连接额度耗光了。咱们来一步步搞定它:
核心问题根源
Vapor的数据库连接默认靠连接池管理,但如果在WebSocket的闭包里直接持有连接(比如用req.db或者手动获取的连接),当WebSocket关闭时,连接可能因为闭包的引用关系无法自动归还给连接池,最终导致连接耗尽。
解决方案步骤
1. 用withConnection自动管理连接生命周期(最推荐)
Vapor提供了withConnection方法,它会自动帮你获取连接,执行完数据库操作后立刻把连接归还给连接池——不管操作成功还是抛出错误,都能保证连接被释放,完全不用手动操心:
app.webSocket("ws") { req, ws in // 监听WebSocket文本消息 ws.onText { ws, text in // 用withConnection包裹所有数据库操作 try await req.db.withConnection { db in // 执行你的存储逻辑,比如保存消息到数据库 let message = Message(content: text) try await message.save(on: db) // 给客户端发送确认消息 try await ws.send("Message saved successfully!") } } // 监听WebSocket关闭事件,做额外日志或清理 ws.onClose.whenComplete { result in switch result { case .success: print("WebSocket closed normally") case .failure(let error): print("WebSocket closed with error: \(error.localizedDescription)") } // 这里不需要手动释放连接,withConnection已经处理好了 } }
2. 手动获取+显式释放(备选方案)
如果因为某些原因不能用withConnection,可以手动获取连接,并用defer确保无论如何都会释放:
app.webSocket("ws") { req, ws in ws.onText { ws, text in // 手动获取数据库连接 let db = try await req.db.connect() // 用defer保证连接一定会被释放 defer { try? db.release() } // 执行数据库操作 let message = Message(content: text) try await message.save(on: db) try await ws.send("Message saved!") } }
这种方式一定要注意defer的位置,确保覆盖所有代码路径。
3. 限制连接池大小,避免超过Heroku额度
Heroku只给20个数据库连接,Vapor默认的连接池大小可能超过这个数,所以要手动配置连接池上限,留1-2个连接给其他操作(比如后台任务):
// 在configure.swift中配置PostgreSQL时,设置连接池大小 app.databases.use(.postgres( hostname: Environment.get("DATABASE_HOST") ?? "localhost", port: Environment.get("DATABASE_PORT").flatMap(Int.init) ?? PostgresConfiguration.ianaPortNumber, username: Environment.get("DATABASE_USER") ?? "vapor", password: Environment.get("DATABASE_PASSWORD") ?? "vapor", database: Environment.get("DATABASE_NAME") ?? "vapor" ), as: .postgres) // 调整连接池最大连接数 if let postgresPool = app.databases.pool(for: .postgres) as? DatabasePool { postgresPool.configuration.maxConnections = 18 }
4. 排查全局持有连接的情况
检查代码里有没有全局变量持有Database实例,或者在WebSocket闭包外创建了连接但没释放的情况——这类隐蔽的泄漏也会慢慢耗光连接额度。
验证方法
部署后,可以用Heroku命令查看当前数据库连接数,确认连接不再泄漏:
heroku pg:info
也可以在代码里加日志,记录连接的获取和释放情况,跟踪WebSocket关闭后的连接变化。
内容的提问来源于stack exchange,提问作者Nevin Jethmalani
相关产品推荐
相关产品推荐

