高负载WebSocket应用卡顿如何解决?附Gorilla Websocket实现代码
1分钟断开原因
- 没有配置Gorilla WebSocket的心跳保活机制:Gorilla WebSocket默认读超时为60秒,你代码没有设置
PongHandler,也没有主动向客户端发送Ping帧,当连接超过60秒没有收到客户端的任何消息时,服务端会主动断开连接,这就是新连接稳定1分钟左右断开的直接原因。
卡顿阻塞原因
- 互斥锁持有期间存在可能永久阻塞的操作:你在持有全局互斥锁
tm.mx的状态下向Collectingchan写数据,如果该通道是无缓冲通道且消费端处理速度跟不上,或者消费端已经异常退出,写通道的操作会永久阻塞,导致tm.mx锁无法释放。后续所有需要操作连接列表、订阅/取消订阅股票的请求都会因为拿不到锁被卡住,整个服务进入假死状态,不再推送数据。 - 连接清理逻辑性能极差:
removeConnection方法删除一个连接时,需要遍历全局所有股票的所有客户端连接,当订阅的股票数量和连接数上来后,该操作耗时会指数级上升,且执行期间全程持有全局锁,会进一步放大阻塞问题。
其他潜在并发问题
- HTTP Handler签名非法:
http.HandleFunc要求注册的Handler签名必须是func(http.ResponseWriter, *http.Request),你当前的echo方法多了Collectingchan参数,该注册逻辑实际无法正常编译运行,推测是你代码中Collectingchan是TicksModel的成员变量,贴代码时错误写成了入参,建议修正该问题,把Collectingchan放到结构体中统一管理。 - 并发修改
Upgrader配置:你在每个连接的Handler中都重新赋值tm.upgrader.CheckOrigin,该操作不是线程安全的,高并发下会出现并发读写问题,导致连接升级异常,建议在初始化TicksModel时就设置好CheckOrigin,不要在请求处理逻辑中修改。 - 死锁风险:
writeMessages方法写消息失败时会调用removeConnection拿全局锁,而如果此时有其他协程已经持有锁在写Collectingchan阻塞,就会出现死锁。
内容的提问来源于stack exchange,提问作者Nurislom Rakhmatullaev
相关产品推荐
相关产品推荐

