一、问题现象与典型场景
在使用uvicorn部署WebSocket服务时,ConnectionClosedError是最常见的协议层异常之一。当客户端非正常断开连接时,服务端会抛出该异常,控制台通常显示:
websockets.exceptions.ConnectionClosedError: code = 1006 (connection closed abnormally), reason = ''
该问题主要出现在以下场景:
- 移动端网络不稳定导致连接中断
- 客户端未发送关闭帧直接退出
- Nginx/TCP层超时断开空闲连接
- 浏览器标签页关闭未触发onclose事件
二、根本原因分析
通过对uvicorn源码的分析发现,WebSocketProtocol底层依赖websockets库实现RFC 6455协议。当出现以下情况时会触发异常:
- 协议违规:未收到完整的关闭握手帧
- 心跳超时:keepalive_ping_timeout设置过短
- 缓冲区溢出:max_queue值设置不合理
三、6种解决方案与代码实现
方案1:全局异常捕获
async def websocket_endpoint(websocket):
try:
while True:
data = await websocket.receive()
# 业务逻辑处理
except websockets.exceptions.ConnectionClosedError:
logging.warning("Client disconnected abruptly")
方案2:调整协议参数
app = FastAPI()
@app.websocket("/ws")
async def websocket(
websocket: WebSocket,
ping_timeout: Optional[int] = 20,
max_queue: Optional[int] = 1024
):
await websocket.accept()
websocket._ping_interval = ping_timeout
websocket._max_queue = max_queue
方案3:实现重连机制
客户端应实现指数退避重连算法:
let reconnectDelay = 1000;
function connect() {
const ws = new WebSocket(url);
ws.onclose = () => {
setTimeout(connect, Math.min(reconnectDelay *= 2, 30000));
};
}
四、进阶优化建议
| 配置项 | 推荐值 | 作用 |
|---|---|---|
| ws_ping_interval | 20s | 心跳检测间隔 |
| ws_max_size | 16MB | 单帧最大尺寸 |
通过结合连接状态监控和自适应超时调整,可以降低异常发生率。建议在生产环境部署APM工具监控以下指标:
- WebSocket连接存活时间分布
- 异常断开的原因分类统计
- 消息吞吐量与延迟百分位