MQ架构设计
源代码1:
⚠️ 核心问题 1:你在用
⚠️ 核心问题 4:你用的是
🔴 问题1:
// ************************************************************************ // // Copyright © 2010-2018 wuhan wangyou tec.Corporation,All Rights Reserved. // 版权所有: 武汉网友科技有限公司 // // 作者(Author): 毛善丽 // // 数据表(Table): // // 创建日期(Create Time): // // 说明(Description): // // 修改记录(Revision History): // R1: // // ************************************************************************ using RabbitMQ.Client; using System; using System.Collections.Generic; using System.Text; using SysEngine; using RabbitMQ.Client.Events; using System.Threading.Tasks; using BaseDll; namespace DealPornImage { public class RabbitMQMan { public string szHostName = ""; public int iPort = 0; public string szUserName = ""; public string szPassWord = ""; public string szReceiveKey = ""; public string szEnvirment = ""; public Dele_Void_1Param Callback; public IConnectionFactory MQFactory; private IConnection MQConnection = null; private IModel MQQueue = null; public RabbitMQMan(string szHostName, int iPort, string szUserName, string szPassWord, string queKey) { this.szHostName = szHostName; this.iPort = iPort; this.szUserName = szUserName; this.szPassWord = szPassWord; this.szReceiveKey = "relay.integrate" + queKey; MQFactory = new ConnectionFactory { HostName = szHostName, Port = iPort, UserName = szUserName, Password = szPassWord, AutomaticRecoveryEnabled = true, }; } public void Start() { //szReceiveKey = "relay.integrate"+szEnvirment; MQReceive(); } public void Stop() { MQConnection.Dispose(); MQConnection = null; } private void CreateConnection(string queuekey) { try { if (MQConnection == null) MQConnection = MQFactory.CreateConnection(); if (MQQueue == null) { MQQueue = MQConnection.CreateModel(); MQQueue.QueueDeclare( queue: queuekey,//消息队列名称 durable: false,//是否缓存 exclusive: false, autoDelete: false, arguments: null ); } } catch (Exception ex) { LogManager.debug.Info(ex.ToString()); } return; } public void MQReceive() { CreateConnection(szReceiveKey); if (MQQueue == null) return; while (true) { BasicGetResult bgr = MQQueue.BasicGet(szReceiveKey, true); //LogManager.info.Info("获取到MQ数据包"); if (bgr == null) break; byte[] message =bgr.Body; LiaoPacket packet = new LiaoPacket(); packet.SetBufferPtr(message); ushort cmd = 0; packet.GetShortInt(10, ref cmd); //LogManager.debug.Info(cmd.ToString()); //获取聊号,语音类型 if (InfoBase.manager.m_infoBase.ContainsKey(cmd)) InfoBase.manager.m_infoBase[cmd].RQCallBack(packet); } } public void SendPacket(LiaoPacket packet, string queuekey) { CreateConnection(queuekey); if (MQQueue == null) return; // using (IModel channel = MQConnection.CreateModel()) { byte[] body = new byte[packet.DataLen]; Buffer.BlockCopy(packet.DataBuffer, 0, body, 0, packet.DataLen); MQQueue.BasicPublish("", queuekey, null, body); } } } }

参考E:\RabbitMQ事件驱动分布式中转架构
源代码2:存在的问题与MQ连接断开无法重连,没有数据发送断开连接且数据来后不会自动恢复连接。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BaseDll;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using SysEngine;
namespace RelaySvrAssist
{
public class MQOuterData
{
public int roomid { get; set; } = 0;
public string lid { get; set; } = "";
}
class ServiceForMQ
{
#region 单件模式
private static ServiceForMQ m_instance;
public static ServiceForMQ Inst
{
get
{
if (m_instance == null)
m_instance = new ServiceForMQ();
return m_instance;
}
}
private ServiceForMQ()
{}
#endregion
#region 变量属性
private ServiceBase SvcQueue = new ServiceBase();
private ConnectionFactory MQFactory = new ConnectionFactory();
private IConnection MQConnection = null;
private IModel MQChannel = null;
private IBasicConsumer MQConsumer = null;
private DateTime m_connTime = DateTime.MinValue;
public string MQHostName { get; set; } = "192.168.9.199";
public string MQUserName { get; set; } = "admin";
public string MQUserPswd { get; set; } = "1";
public int MQHostPort { get; set; } = 5672;
public string MQEnvironment { get; set; } = "";
public string MQQueueReceive_Svr { get; set; } = "";
//中心
public string MQQueueSend_Svr = "relay.integrate";
//中转
#endregion
#region 公共函数
public void Start(uint iSvrId)
{
if (MQConnection != null)
return;
MQFactory.HostName = MQHostName;
MQFactory.UserName = MQUserName;
MQFactory.Password = MQUserPswd;
MQFactory.Port = MQHostPort;
MQFactory.VirtualHost = "/";
MQFactory.Protocol = Protocols.DefaultProtocol;
MQQueueReceive_Svr = "relay.svr." + iSvrId;
LogManager.debug.Info($"[MQ连接信息][ip地址{MQHostName}][端口号{MQHostPort}][密码{MQUserPswd}][用户名{MQUserName}][服务器id{iSvrId}]");
LogManager.debug.Info($"[MQ发包队列][{ServiceForMQ.Inst.MQQueueSend_Svr}][收包队列][{ServiceForMQ.Inst.MQQueueReceive_Svr}]");
CreateConnection(MQQueueReceive_Svr);
}
public void Stop()
{
if (MQConnection == null)
return;
MQConnection.Dispose();
MQConnection = null;
MQChannel.Dispose();
MQChannel = null;
}
public async Task MQSend(string strQueueKey, LiaoPacket varpacket)
{
await SvcQueue.Call(MQSend_Internal, strQueueKey, varpacket).ConfigureAwait(false);
}
public async Task MQReceive(NetSession session)
{
await SvcQueue.Call(MQReceive_Internal, MQQueueReceive_Svr, session).ConfigureAwait(false);
}
#endregion
#region 私有函数
private void CreateConnection(string strQueueKey)
{
//若连接失败,隔60s重连
try
{
if (MQConnection == null || !MQConnection.IsOpen)
{
MQConnection?.Dispose();
MQConnection = MQFactory.CreateConnection();
LogManager.debug.Info("[MQ启动成功]");
}
if (MQChannel == null || !MQChannel.IsOpen)
{
MQChannel?.Dispose();
MQChannel = MQConnection.CreateModel();
MQChannel.QueueDeclare(strQueueKey, false, false, false, null);
MQConsumer = new RabbitMQ.Client.DefaultBasicConsumer(MQChannel);
}
if (MQChannel == null)
return;
}
catch(Exception ex)
{
LogManager.debug.Info($"[MQ启动失败][{ex.ToString()}]");
return;
}
//QueueDeclare参数
//queue - 队列唯一名称
//durable - 消息队列是否持久化(重启MQ, 消息队列也不丢失)
// 另外: 消息持久化方法
// var properties = channel.CreateBasicProperties();
// properties.SetPersistent(true);
//exclusive
//autoDelete
//arguments -
//MQChannel.BasicConsume(
// queue: MQQueueName_Svr,
// autoAck: false,
// consumer: MQConsumer);
}
private async Task MQReceive_Internal(string strQueueKey, NetSession session)
{
try
{
CreateConnection(strQueueKey);
if (MQChannel == null)
return;
while (true)
{
BasicGetResult bgr = null;
try
{
bgr = MQChannel.BasicGet(strQueueKey, true);
}
catch (Exception ex)
{
LogManager.debug.Info($"[MQ BasicGet 失败,准备重试]{ex}");
await Task.Delay(500); // 避免疯狂重试
break; // 让外层逻辑重新进入 MQReceive 再重建连接
}
if (bgr == null)
break;
// 1. 先把 byte[] 转成字符串
string json = Encoding.UTF8.GetString(bgr.Body.ToArray());
MQOuterData data = new MQOuterData();
try
{
if (!string.IsNullOrWhiteSpace(json))
{
// 尝试解析 JSON,如果失败不会抛异常,data 保持默认值
data = JsonConvert.DeserializeObject<MQOuterData>(json) ?? data;
}
}catch(JsonException ex)
{
LogManager.debug.Info($"JSON解析失败: {ex.Message}, 原始数据: {json.Replace('{', '|').Replace('}', '|')}");
}
LogManager.info.Info(json.Replace('{','|').Replace('}','|'));
LiaoPacket sendpacket = new LiaoPacket();
sendpacket.SetShortInt(10, LiaoCmd.CMD_RE_MQ_GET_MSG);
sendpacket.SetString(20, json);
sendpacket.SetInt(30, data.roomid);
sendpacket.SetString(40, data.lid);
session.SendPacket(sendpacket);
}
}
catch (Exception ex)
{
LogManager.fatal.Info($"[未知异常]{ex.ToString()}");
}
}
private async Task MQSend_Internal(string strQueueKey, LiaoPacket varpacket)
{
try
{
CreateConnection(strQueueKey);
if (MQChannel == null)
return;
byte[] btsend = new byte[varpacket.DataLen];
Buffer.BlockCopy(varpacket.DataBuffer, 0, btsend, 0, varpacket.DataLen);
// LogManager.test.Info($"发送数据包大小{varpacket.DataLen}");
MQChannel.BasicPublish("", strQueueKey, null, btsend);
}
catch (Exception ex)
{
LogManager.fatal.Info($"[未知异常]{ex.ToString()}");
}
}
#endregion
}
}
⚠️ 核心问题 1:你在用 BasicGet 轮询(这是大坑)
你现在是:
while (true)
{
bgr = MQChannel.BasicGet(strQueueKey, true);
if (bgr == null)
break;
}
{
bgr = MQChannel.BasicGet(strQueueKey, true);
if (bgr == null)
break;
}
👉 这会导致:
- 没数据 → 立刻 break
- 上层再调 MQReceive → 再进来 → 再 break
- 形成“伪轮询 + 空转 + 连接频繁空闲”
❗副作用
- RabbitMQ 认为这个连接长时间 idle
- TCP KeepAlive / Heartbeat 没触发
- 服务器主动断开连接
⚠️ 核心问题 2:你没有开启 Heartbeat(致命)
你现在:
MQFactory.Protocol = Protocols.DefaultProtocol;
👉 但你没有设置:
MQFactory.RequestedHeartbeat = TimeSpan.FromSeconds(30);
❗结果
- 默认 heartbeat 可能是 0(禁用)
- 一旦连接空闲:
👉 服务端会直接断开
⚠️ 核心问题 3:你没有自动恢复连接
RabbitMQ 官方推荐必须开:
MQFactory.AutomaticRecoveryEnabled = true;
MQFactory.NetworkRecoveryInterval = TimeSpan.FromSeconds(10);
MQFactory.NetworkRecoveryInterval = TimeSpan.FromSeconds(10);
否则:
👉 一旦断线 = 永久死亡连接
⚠️ 核心问题 4:你用的是 BasicGet + autoAck=true
bgr = MQChannel.BasicGet(strQueueKey, true);
问题:
- autoAck=true → 消息直接确认
- 如果你处理中断 → 消息丢失
- 更重要:不会保持消费状态
⚠️ 核心问题 5:你其实根本没“消费”,只是“拉取”
RabbitMQ 正确姿势是:
👉 用 BasicConsume(事件驱动)
而不是:
👉 BasicGet(轮询)
基于源代码2做优化:保留轮询获取数据
✔ 1. 必须开启 Heartbeat(最关键)
在 Start() 里加:
MQFactory.RequestedHeartbeat = TimeSpan.FromSeconds(30);
MQFactory.KeepAlive = true;
MQFactory.KeepAlive = true;
👉 否则:
- 只要一段时间没数据
- TCP idle
- 服务端直接断连接(你现在就是这个情况)
✔ 2. 开启自动重连(必须)
MQFactory.AutomaticRecoveryEnabled = true;
MQFactory.NetworkRecoveryInterval = TimeSpan.FromSeconds(10);
MQFactory.NetworkRecoveryInterval = TimeSpan.FromSeconds(10);
👉 否则断了之后你这句:
if (MQConnection == null || !MQConnection.IsOpen)
很多时候感知不到真实断开状态(RabbitMQ 客户端不会立刻标记)
✔ 3. 你的轮询逻辑要“降速”,否则连接会被打死
你现在:
if (bgr == null)
break;
break;
👉 问题:
- 外层不断调 MQReceive
- 实际是高频空轮询
- RabbitMQ + 网络层会认为你是异常连接
第3点,因为是低频轮询,暂且可以保留,如果轮询高频,需要修改。
✔ 4. 定期“保活”(防止空闲断开)
👉 即使你开了 heartbeat,有些环境(云 / NAT / 防火墙)还是会断
简单粗暴方案(推荐)
加一个定时任务,每 20~30 秒:
public void KeepAlive()
{
try
{
if (MQChannel != null && MQChannel.IsOpen)
{
MQChannel.BasicQos(0, 1, false); // 任意轻量操作即可
}
}
catch {}
}
{
try
{
if (MQChannel != null && MQChannel.IsOpen)
{
MQChannel.BasicQos(0, 1, false); // 任意轻量操作即可
}
}
catch {}
}
👉 或者更简单:
var _ = MQConnection.IsOpen;
✔ 5. 修复你的连接重建逻辑(隐藏大坑)
你现在:
if (MQConnection == null || !MQConnection.IsOpen)
👉 问题:
RabbitMQ 有个坑:
连接已经死了,但 IsOpen 还是 true(特别是网络断开)
🔴 问题1:IsOpen 判断仍然不可靠(核心隐患)
你现在:
if (MQConnection == null || !MQConnection.IsOpen)
👉 这个在 RabbitMQ 里是经典坑:
❗ TCP 断了,但
IsOpen == true
💥 结果
- 你以为连接是好的
- 实际已经死了
- 然后:
BasicGet抛异常 ✔(你 catch 了)- 但有时候不会立刻抛 ❗ → 直接卡死
✅ 正确修复(必须改)
把 CreateConnection 改成这样👇(关键点)
private void CreateConnection(string strQueueKey)
{
try
{
bool needReconnect = false;
if (MQConnection == null)
{
needReconnect = true;
}
else
{
try
{
// ✅ 关键:触发底层检查
var test = MQConnection.IsOpen;
}
catch
{
needReconnect = true;
}
}
if (needReconnect)
{
MQConnection?.Dispose();
MQConnection = MQFactory.CreateConnection();
LogManager.debug.Info("[MQ连接创建成功]");
}
if (MQChannel == null || !MQChannel.IsOpen)
{
MQChannel?.Dispose();
MQChannel = MQConnection.CreateModel();
MQChannel.QueueDeclare(strQueueKey, false, false, false, null);
}
}
catch (Exception ex)
{
LogManager.debug.Info($"[MQ启动失败][{ex}]");
}
}
{
try
{
bool needReconnect = false;
if (MQConnection == null)
{
needReconnect = true;
}
else
{
try
{
// ✅ 关键:触发底层检查
var test = MQConnection.IsOpen;
}
catch
{
needReconnect = true;
}
}
if (needReconnect)
{
MQConnection?.Dispose();
MQConnection = MQFactory.CreateConnection();
LogManager.debug.Info("[MQ连接创建成功]");
}
if (MQChannel == null || !MQChannel.IsOpen)
{
MQChannel?.Dispose();
MQChannel = MQConnection.CreateModel();
MQChannel.QueueDeclare(strQueueKey, false, false, false, null);
}
}
catch (Exception ex)
{
LogManager.debug.Info($"[MQ启动失败][{ex}]");
}
}
🔴 问题2:你现在是“被动重连”,缺少主动保活
你现在完全依赖:
👉 BasicGet 出异常 → 才重连
但问题是:
❗ 有一种情况:连接死了,但一直不抛异常
(网络半断、NAT回收、云环境特别常见)
✅ 加一个“轻量保活”(非常重要)
加个函数:
public void TickAlive()
{
try
{
if (MQConnection == null)
return;
// ✅ 触发底层 socket 活动
var _ = MQConnection.IsOpen;
}
catch
{
ErrorDispose();
}
}
{
try
{
if (MQConnection == null)
return;
// ✅ 触发底层 socket 活动
var _ = MQConnection.IsOpen;
}
catch
{
ErrorDispose();
}
}
👉 在你的主循环 / 定时器里调用
比如每 10~20 秒:
ServiceForMQ.Inst.TickAlive();
🔴 问题3:缺少“连接创建限速”(防止雪崩)
你现在:
MQConnection = MQFactory.CreateConnection();
👉 如果 MQ 挂了:
- 每次调用都会尝试连接
- 高并发下 → 疯狂重连风暴
✅ 建议加一个简单限流
private DateTime _lastConnTime = DateTime.MinValue;
然后:
if (needReconnect)
{
if ((DateTime.Now - _lastConnTime).TotalSeconds < 5)
return;
_lastConnTime = DateTime.Now;
MQConnection?.Dispose();
MQConnection = MQFactory.CreateConnection();
LogManager.debug.Info("[MQ连接创建成功]");
}
{
if ((DateTime.Now - _lastConnTime).TotalSeconds < 5)
return;
_lastConnTime = DateTime.Now;
MQConnection?.Dispose();
MQConnection = MQFactory.CreateConnection();
LogManager.debug.Info("[MQ连接创建成功]");
}
修复后的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BaseDll;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using SysEngine;
namespace RelaySvrAssist
{
public class MQOuterData
{
public int roomid { get; set; } = 0;
public string lid { get; set; } = "";
}
class ServiceForMQ
{
#region 单件模式
private static ServiceForMQ m_instance;
public static ServiceForMQ Inst
{
get
{
if (m_instance == null)
m_instance = new ServiceForMQ();
return m_instance;
}
}
private ServiceForMQ()
{}
#endregion
#region 变量属性
private ServiceBase SvcQueue = new ServiceBase();
private ConnectionFactory MQFactory = new ConnectionFactory();
private IConnection MQConnection = null;
private IModel MQChannel = null;
private IBasicConsumer MQConsumer = null;
private DateTime m_connTime = DateTime.MinValue;
private DateTime _lastConnTime = DateTime.MinValue;
public string MQHostName { get; set; } = "192.168.9.199";
public string MQUserName { get; set; } = "admin";
public string MQUserPswd { get; set; } = "1";
public int MQHostPort { get; set; } = 5672;
public string MQEnvironment { get; set; } = "";
public string MQQueueReceive_Svr { get; set; } = "";
//中心
public string MQQueueSend_Svr = "relay.integrate";
//中转
#endregion
#region 公共函数
public void Start(uint iSvrId)
{
if (MQConnection != null)
return;
MQFactory.HostName = MQHostName;
MQFactory.UserName = MQUserName;
MQFactory.Password = MQUserPswd;
MQFactory.Port = MQHostPort;
MQFactory.VirtualHost = "/";
MQFactory.Protocol = Protocols.DefaultProtocol;
MQFactory.RequestedHeartbeat =30;
MQFactory.AutomaticRecoveryEnabled = true;
try
{
MQFactory.NetworkRecoveryInterval = TimeSpan.FromSeconds(10);
}
catch { }
MQQueueReceive_Svr = "relay.svr." + iSvrId;
LogManager.debug.Info($"[MQ连接信息][ip地址{MQHostName}][端口号{MQHostPort}][密码{MQUserPswd}][用户名{MQUserName}][服务器id{iSvrId}]");
LogManager.debug.Info($"[MQ发包队列][{ServiceForMQ.Inst.MQQueueSend_Svr}][收包队列][{ServiceForMQ.Inst.MQQueueReceive_Svr}]");
CreateConnection(MQQueueReceive_Svr);
}
public void Stop()
{
if (MQConnection == null)
return;
MQConnection.Dispose();
MQConnection = null;
MQChannel.Dispose();
MQChannel = null;
}
public async Task MQSend(string strQueueKey, LiaoPacket varpacket)
{
await SvcQueue.Call(MQSend_Internal, strQueueKey, varpacket).ConfigureAwait(false);
}
public async Task MQReceive(NetSession session)
{
await SvcQueue.Call(MQReceive_Internal, MQQueueReceive_Svr, session).ConfigureAwait(false);
}
#endregion
#region 私有函数
private void CreateConnection(string strQueueKey)
{
try
{
bool needReconnect = false;
if (MQConnection == null)
{
needReconnect = true;
}
else
{
try
{
// ✅ 关键:触发底层检查
var test = MQConnection.IsOpen;
}
catch
{
needReconnect = true;
}
}
if (needReconnect)
{
if ((DateTime.Now - _lastConnTime).TotalSeconds < 5)
return;
_lastConnTime = DateTime.Now;
MQConnection?.Dispose();
MQConnection = MQFactory.CreateConnection();
LogManager.debug.Info("[MQ连接创建成功]");
}
if (MQChannel == null || !MQChannel.IsOpen)
{
MQChannel?.Dispose();
MQChannel = MQConnection.CreateModel();
MQChannel.QueueDeclare(strQueueKey, false, false, false, null);
}
}
catch (Exception ex)
{
LogManager.debug.Info($"[MQ启动失败][{ex}]");
}
}
private async Task MQReceive_Internal(string strQueueKey, NetSession session)
{
try
{
CreateConnection(strQueueKey);
var channel = MQChannel;
if (channel == null)
return;
while (true)
{
BasicGetResult bgr = null;
try
{
bgr = channel.BasicGet(strQueueKey, true);
}
catch (Exception ex)
{
LogManager.debug.Info($"[MQ BasicGet 失败,准备重试]{ex}");
await Task.Delay(500); // 避免疯狂重试
break; // 让外层逻辑重新进入 MQReceive 再重建连接
}
if (bgr == null)
{
break;
}
// 1. 先把 byte[] 转成字符串
string json = Encoding.UTF8.GetString(bgr.Body.ToArray());
MQOuterData data = new MQOuterData();
if (!string.IsNullOrWhiteSpace(json))
{
// 尝试解析 JSON,如果失败不会抛异常,data 保持默认值
data = JsonConvert.DeserializeObject<MQOuterData>(json) ?? data;
LogManager.info.Info(json.Replace('{', '|').Replace('}', '|'));
LiaoPacket sendpacket = new LiaoPacket();
sendpacket.SetShortInt(10, LiaoCmd.CMD_RE_MQ_GET_MSG);
sendpacket.SetString(20, json);
sendpacket.SetInt(30, data.roomid);
sendpacket.SetString(40, data.lid);
session.SendPacket(sendpacket);
}
}
}
catch (Exception ex)
{
LogManager.fatal.Info($"[未知异常]{ex.ToString()}");
ErrorDispose();
}
}
private async Task MQSend_Internal(string strQueueKey, LiaoPacket varpacket)
{
try
{
CreateConnection(strQueueKey);
var channel = MQChannel;
if (channel == null)
return;
byte[] btsend = new byte[varpacket.DataLen];
Buffer.BlockCopy(varpacket.DataBuffer, 0, btsend, 0, varpacket.DataLen);
// LogManager.test.Info($"发送数据包大小{varpacket.DataLen}");
channel.BasicPublish("", strQueueKey, null, btsend);
}
catch (Exception ex)
{
LogManager.fatal.Info($"[未知异常]{ex.ToString()}");
ErrorDispose();
}
}
private void ErrorDispose()
{
// ✅ 出错强制断开,触发重连
MQChannel?.Dispose();
MQChannel = null;
MQConnection?.Dispose();
MQConnection = null;
}
public void TickAlive()
{
try
{
if (MQConnection == null)
return;
// ✅ 触发底层 socket 活动
var _ = MQConnection.IsOpen;
}
catch
{
ErrorDispose();
}
}
#endregion
}
}

浙公网安备 33010602011771号