独立部署OpenIM遇到的坑(1)上下线卡住
公司项目有im场景,我们自己在k8s中部署了3.5.1版本的openim,在开发和运维的过程中,我们遇到了一些问题,这里记录一下问题详情和解决的过程。 问题触发场景 我们编写了一个压力测试程序,模拟用户的典型使用场景: 建立连接(上线) 发送消息 接收回复 断开连接(下线) 测试设置:100个并发用户账号,每个账号持续重复上述流程,模拟高频上下线场景。 问题现象 压力测试程序运行一段时间后(通常几分钟到几十分钟),开始出现以下现象: 新用户连接请求无响应,卡在WebSocket握手阶段 已连接用户无法正常下线 服务器CPU使用率正常,但连接数不再变化 重启openim-msggateway服务后问题暂时恢复 原因 先说结论:上下线排队处理,导致了多个管道(channel)之间的死锁。 以下是详细解释。 为了理解这个问题,我们需要先了解OpenIM的连接链路。 这里说的上线是指openim服务器接收客户端建立的长连接,长连接采用websocket实现。长连接建立好之后,客户端和服务器就可以向对方发送消息了。 同一个客户端端长连接链路上有两个服务器:openim-msggateway-proxy和openim-msggateway openim-msggateway-proxy自身没有什么业务逻辑,它起到长连接负载均衡的作用,方便openim-msggateway的扩容。 问题出现在openim-msggateway,它是负责处理向客户端的长连接收发消息的核心服务。 具体地,在WsServer中定义的registerChan和unregisterChan互相之间产生了消费和生产的依赖关系,导致了死锁。 让我们来看一下相关的代码。 WsServer中定义了registerChan和unregisterChan: 1 2 3 4 5 6 7 // internal/msggateway/n_ws_server.go type WsServer struct { // ... registerChan chan *Client // 处理上线的channel unregisterChan chan *Client // 处理下线的channel // ... } 初始化为大小为1000的有缓冲channel 1 2 3 4 5 6 7 8 9 10 // internal/msggateway/n_ws_server.go func NewWsServer(globalConfig *config.GlobalConfig, opts ...Option) (*WsServer, error) { // ... return &WsServer{ // ... registerChan: make(chan *Client, 1000), unregisterChan: make(chan *Client, 1000), // ... }, nil } 用户建立websocket连接时,会触发向ws.registerChan中写入数据 ...