.NET6 RabbitMQ自动重连

序言

        在技术领域,不断学习和探索是保持竞争力的关键。最近,我重新开始了对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,//自动重连
                To
<think>我们正在使用.NET Core与RabbitMQ集群进行交互。根据引用[4]和[5],RabbitMQ集群可以提供高可用性,当某个节点故障时,其他节点可以接管工作。在.NET Core中,我们可以通过RabbitMQ.Client库来连接集群。 关键步骤: 1. 安装RabbitMQ.Client NuGet包。 2. 创建连接工厂(ConnectionFactory),并设置用户名、密码等参数。 3. 在创建连接时,传入集群中多个节点的地址(可以是IP或主机名)。 4. 设置自动恢复选项(如AutomaticRecoveryEnabled和TopologyRecoveryEnabled)以增强可靠性。 参考引用[5]中的代码示例,我们可以这样配置: ```csharp var connectionFactory = new ConnectionFactory { UserName = "guest", // 用户名 Password = "guest", // 密码 AutomaticRecoveryEnabled = true, // 自动新连接 TopologyRecoveryEnabled = true // 恢复拓扑(交换器、队列等) }; // 使用集群节点地址列表创建连接 var connection = connectionFactory.CreateConnection(new string[] { "192.168.23.149", "192.168.23.144", "192.168.23.143" }); // 创建通道 var channel = connection.CreateModel(); ``` 这样,客户端会尝试连接到列表中的节点,直到成功连接到一个节点。如果当前连接的节点失败,客户端会自动尝试连接到其他节点。 注意事项: - 集群中的所有节点必须使用相同的Erlang cookie(确保节点间通信安全)。 - 在代码中配置多个节点地址,可以增加连接成功的几率。 - 建议开启自动恢复功能,以便在网络波动或节点故障时自动恢复连接和拓扑结构。 §§相关问题§§ 1. 如何确保RabbitMQ集群中节点间数据同步? 2..NET Core中如何处理RabbitMQ连接断开和连? 3. RabbitMQ集群的负载均衡策略有哪些?</think>### .NET Core 使用 RabbitMQ 集群配置指南 在 .NET Core 中配置 RabbitMQ 集群需遵循以下步骤: #### 1. **RabbitMQ 集群部署** - 所有节点需使用相同的 Erlang cookie(位于 `/var/lib/rabbitmq/.erlang.cookie`) - 通过命令将节点加入集群: ```bash rabbitmqctl stop_app rabbitmqctl join_cluster rabbit@node1 # 主节点名 rabbitmqctl start_app ``` - 验证集群状态:`rabbitmqctl cluster_status`[^3] #### 2. **.NET Core 客户端配置** ```csharp var factory = new ConnectionFactory { UserName = "your_username", Password = "your_password", AutomaticRecoveryEnabled = true, // 启用自动连 TopologyRecoveryEnabled = true, // 恢复队列/交换机绑定 VirtualHost = "/" }; // 集群节点地址列表 var endpoints = new List<AmqpTcpEndpoint> { new AmqpTcpEndpoint("192.168.1.101"), new AmqpTcpEndpoint("192.168.1.102"), new AmqpTcpEndpoint("192.168.1.103") }; // 创建集群连接 using var connection = factory.CreateConnection(endpoints); using var channel = connection.CreateModel(); ``` 关键参数说明: - `AutomaticRecoveryEnabled`:节点故障时自动连其他节点 - `TopologyRecoveryEnabled`:保持队列/交换机配置一致性[^5] #### 3. **消息生产与消费** ```csharp // 声明队列(所有节点自动同步) channel.QueueDeclare( queue: "orders", durable: true, // 持久化队列 exclusive: false, autoDelete: false ); // 发送消息 var body = Encoding.UTF8.GetBytes("订单数据"); channel.BasicPublish( exchange: "", routingKey: "orders", basicProperties: null, body: body ); // 消费消息 var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { var message = Encoding.UTF8.GetString(ea.Body.ToArray()); Console.WriteLine($"收到消息: {message}"); }; channel.BasicConsume(queue: "orders", autoAck: true, consumer); ``` #### 4. **高可用配置** - **镜像队列**(防止节点故障数据丢失): ```bash rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}' ``` - **负载均衡**:客户端自动轮询可用节点[^4] - **网络分区处理**:配置 `cluster_partition_handling = pause_minority` #### 5. **故障处理最佳实践** - 实现 `IConnectionFactory` 自定义试策略 - 监控事件: ```csharp connection.ConnectionShutdown += (sender, args) => { Console.WriteLine($"连接断开原因: {args.ReplyText}"); }; ``` - 使用 TLS 加密集群通信 > **要提示**:所有集群节点需同步时间(NTP),防火墙开放端口:4369(epmd)、5672(AMQP)、25672(Erlang 分布式)[^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值