序言
在技术领域,不断学习和探索是保持竞争力的关键。最近,我重新开始了对RabbitMQ的研究,这个过程让我又收获了许多新的知识和技能,我觉得有必要记录下来,以便将来回顾和分享。
问题引出
RabbitMQ是一款开源的消息队列中间件,它提供了高效、可靠、灵活的通信机制,广泛应用于分布式系统中。然而,有时候在使用RabbitMQ时会遇到连接断开的问题,这可能会导致消息传递中断和应用程序的不可用性。
问题分析
当使用RabbitMQ时,可能会遇到以下几种情况导致连接断开的问题:
1.网络问题:网络中断、防火墙设置等问题可能导致RabbitMQ连接断开。
2.长时间空闲:如果连接在一段时间内没有进行任何通信,RabbitMQ可能会自动关闭连接。
3.RabbitMQ服务器问题:RabbitMQ服务器可能会因为负载过高或其他原因主动关闭连接。
解决方案
虽然RabbitMQ.Client库,有心跳机制,有断线重连机制,但是在网络断开的时候,并不能重连。
下面这段代码经过本人验证有效,可以解决上面的问题。
using RabbitMQ.Client.Events;
using RabbitMQ.Client;
using System.Text;
using RabbitMQDemo.Shared;
using System.Collections.Concurrent;
using RabbitMQ.Client.Exceptions;
namespace RabbitMQConsumerDemo
{
public class RabbitMQRpcClientHandler
{
/// <summary>
/// 定义一个静态变量来保存类的实列
/// </summary>
private static RabbitMQRpcClientHandler? _uniqueInstance;
/// <summary>
/// 定义一个标识确保线程同步
/// </summary>
private static readonly object _locker = new();
/// <summary>
/// Main entry point to the RabbitMQ .NET AMQP client API. Constructs RabbitMQ.Client.IConnection instances.
/// </summary>
private static IConnectionFactory? _factory;
/// <summary>
/// Main interface to an AMQP connection.
/// </summary>
private IConnection? _connection;
/// <summary>
/// 发送通道(发送通道及接收通道分开,避免互相影响,导致整个服务不可用)
/// </summary>
private IModel? _sendChannel;
/// <summary>
/// 接收通道(发送通道及接收通道分开,避免互相影响,导致整个服务不可用)
/// </summary>
private IModel? _listenChannel;
/// <summary>
/// 监听消费者
/// </summary>
private EventingBasicConsumer? _listenConsumer;
/// <summary>
/// 响应消费者
/// </summary>
private EventingBasicConsumer? _replyConsumer;
/// <summary>
/// RabbitMQ 主机域名
/// </summary>
private readonly string _defaultRabbitMQHostName = "127.0.0.1";
/// <summary>
/// RabbitMQ 服务器端口, 默认 5672, 网页监控页面是 15672
/// </summary>
private readonly int _defaultRabbitMQPort = 5672;
/// <summary>
/// RabbitMQ 用户名, 默认 guest
/// </summary>
private readonly string _defaultRabbitMQUserName = "guest";
/// <summary>
/// RabbitMQ 密码, 默认 guest
/// </summary>
private readonly string _defaultRabbitMQPassword = "guest!";
/// <summary>
/// 虚拟主机
/// </summary>
private readonly string _defaultRabbitMQVirtualHost = "/";
/// <summary>
/// 交换机
/// </summary>
private readonly string _exchangeName = "";
/// <summary>
/// 数据监控队列
/// </summary>
private readonly string _listenQueueName = "queue.listen.test";
/// <summary>
/// 指令响应队列
/// </summary>
private string _replyQueueName = string.Empty;
/// <summary>
/// 注册-路由键
/// </summary>
private readonly string _routingKeyRegister = "queue.register";
/// <summary>
/// 心跳-路由键
/// </summary>
private readonly string _routingKeyHeart = "queue.heart";
/// <summary>
/// 取消信号
/// </summary>
private readonly CancellationTokenSource _cts = new();
/// <summary>
/// 回调函数映射器
/// </summary>
private readonly ConcurrentDictionary<string, TaskCompletionSource<string>> _callbackMapper = new();
private bool _connectionState;
private bool _sendChannelState;
private bool _listenChannelState;
/// <summary>
/// 连接状态
/// </summary>
public bool ConnectionState
{
get { return _connectionState; }
set
{
if (_connectionState == value)
{
return;
}
_connectionState = value;
if (_connectionState)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: 连接已打开");
}
else
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: 连接已关闭");
}
}
}
/// <summary>
/// 发送通道状态
/// </summary>
public bool SendChannelState
{
get { return _sendChannelState; }
set
{
if (_sendChannelState == value)
{
return;
}
_sendChannelState = value;
if (_sendChannelState)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: 发送通道已打开");
}
else
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: 发送通道已关闭");
}
}
}
/// <summary>
/// 接收通道状态
/// </summary>
public bool ListenChannelState
{
get { return _listenChannelState; }
set
{
if (_listenChannelState == value)
{
return;
}
_listenChannelState = value;
if (_listenChannelState)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: 接收通道已打开");
}
else
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: 接收通道已关闭");
}
}
}
/// <summary>
/// 定义私有构造函数,使外界不能创建该类实例
/// </summary>
private RabbitMQRpcClientHandler()
{
// 创建连接工厂
_factory = new ConnectionFactory()
{
HostName = _defaultRabbitMQHostName,//MQ服务器地址
Port = _defaultRabbitMQPort,//MQ服务端口号
UserName = _defaultRabbitMQUserName,//账号
Password = _defaultRabbitMQPassword,//密码
VirtualHost = _defaultRabbitMQVirtualHost,
RequestedHeartbeat = TimeSpan.FromSeconds(2),
AutomaticRecoveryEnabled = true,//自动重连
TopologyRecoveryEnabled = true,//拓扑重连
NetworkRecoveryInterval = TimeSpan.FromSeconds(10)
};
}
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static RabbitMQRpcClientHan