记一次rabbitmq队列阻塞
一,问题
通过rabbitmq可视化界面看到其中有一个队列未消费数量有5万多,而且在持续增加中
二,分析
1,上网了解了rabbitmq原理后,从未消费的队列中看到unacked一直没有变化,而consumers中有存在消费者,所以应该是程序有收到消息,却一直卡主,没有返回ack给rabbitmq。
2,通过不断测试后发现,程序中确实有一个会发生堵塞地方,就是tcp链接中readline方法。每当有远程主机断开强制断开连接时,有几率发生readline一直堵塞,具体原因未知
三,解决问题
知道了原因,就好解决了,
解决方法1:目前未找到微软有提供的流读取操作有超时设置的地方,因此暂时自己先弄一个线程来对操作进行超时限制,实现如下
/// <summary> /// 超时处理 /// </summary> /// <param name="time">超时时间,单位毫秒</param> /// <param name="func">发生堵塞的方法</param> /// <returns></returns> public static Tuple<bool, string> TimeOutCommand(int time, Func<string> func) { var cts = new CancellationTokenSource(); var token = cts.Token; var task = Task.Factory.StartNew(() => { try { using (token.Register(Thread.CurrentThread.Interrupt)) { return func(); } } catch (Exception) { } return string.Empty; }); var result = string.Empty;//结果 var loop = 100;//等待时间,单位毫秒 var waitTime = 0;//已经等待时间 bool flag = task.Wait(1);//是否执行成功 while (!flag) { waitTime += loop; flag = task.Wait(loop); if (!flag) { if (waitTime > time) { try { cts.Cancel(); } catch (Exception) { } break; } } } if (flag) { result = task.Result; } return new Tuple<bool, string>(flag, result); }
解决方法2:使用TcpClient,设置ReceiveTimeout和SendTimeout超时时间就行
/// <summary> /// 连接远程服务器 /// </summary> public void ReConnect() { if (this.LocalEndPoint != null && this._tcpClient != null) { this.Dispose(); } try { this._tcpClient = new TcpClient(this._host, this._hostPort); this._tcpClient.ReceiveTimeout = 10000; this._tcpClient.SendTimeout = 10000; this._ntkStream = this._tcpClient.GetStream(); this._sReader = new StreamReader(this._ntkStream, Encoding.Default); this.LocalEndPoint = this._tcpClient.Client.LocalEndPoint; this.SetConnectionState(true); } catch (Exception lastError) { this.LastError = lastError; this.SetConnectionState(false); } }
注意:并且服务器有可能会不断的写入空值流,如果不判断的话有可能造成死循环不断读取流