简体   繁体   English

Rabbit MQ - 连接/通道/消费者的恢复

[英]Rabbit MQ - Recovery of connection/channel/consumer

I am creating a consumer that runs in an infinite loop to read messages from the queue.我正在创建一个以无限循环运行的使用者,以从队列中读取消息。 I am looking for advice/sample code on how to recover abd continue within my infinite loop even if there are network disruptions.我正在寻找有关如何在无限循环中恢复 abd continue 的建议/示例代码,即使出现网络中断也是如此。 The consumer has to stay running as it will be installed as a WindowsService.消费者必须保持运行,因为它将作为 WindowsService 安装。

1) Can someone please explain how to properly use these settings? 1)有人可以解释如何正确使用这些设置吗? What is the difference between them?它们之间有什么区别?

NetworkRecoveryInterval 
AutomaticRecoveryEnabled
RequestedHeartbeat

2) Please see my current sample code for the consumer. 2)请查看我当前的消费者示例代码。 I am using the .Net RabbitMQ Client v3.5.6.我正在使用 .Net RabbitMQ Client v3.5.6。

How will the above settings do the "recovery" for me?以上设置将如何为我“恢复”? eg will consumer.Queue.Dequeue block until it is recovered?例如,consumer.Queue.Dequeue 会阻塞直到它被恢复吗? That doesn't seem right so...好像不太对,所以...

Do I have to code for this manually?我必须手动编码吗? eg will consumer.Queue.Dequeue throw an exception for which I have to detect and manually re-create my connection, channel, and consumer?例如,consumer.Queue.Dequeue 会抛出一个异常,我必须检测并手动重新创建我的连接、通道和消费者吗? Or just the consumer, as "AutomaticRecovery" will recover the channel for me?或者只是消费者,因为“AutomaticRecovery”会为我恢复频道?

Does this mean I should move the consumer creation inside the while loop?这是否意味着我应该将消费者创建移动到 while 循环中? what about the channel creation?频道创建呢? and the connection creation?和连接创建?

3) Assuming I have to do some of this recovery code manually, are there event callbacks (and how do I register for them) to tell me that there are network problems? 3) 假设我必须手动执行一些恢复代码,是否有事件回调(以及如何注册它们)告诉我存在网络问题?

Thanks!谢谢!

public void StartConsumer(string queue)
{
            using (IModel channel = this.Connection.CreateModel())
            {
                var consumer = new QueueingBasicConsumer(channel);
                const bool noAck = false;
                channel.BasicConsume(queue, noAck, consumer);

                // do I need these conditions? or should I just do while(true)???
                while (channel.IsOpen &&        
                       Connection.IsOpen &&     
                       consumer.IsRunning)
                {
                    try
                    {
                        BasicDeliverEventArgs item;
                        if (consumer.Queue.Dequeue(Timeout, out item))
                        {
                            string message = System.Text.Encoding.UTF8.GetString(item.Body);
                            DoSomethingMethod(message);
                            channel.BasicAck(item.DeliveryTag, false);
                        }
                    }
                    catch (EndOfStreamException ex)
                    {   
                        // this is likely due to some connection issue -- what am I to do?
                    }
                    catch (Exception ex)
                    {   
                        // should never happen, but lets say my DoSomethingMethod(message); throws an exception
                        // presumably, I'll just log the error and keep on going
                    }
                }
            }
}

        public IConnection Connection
        {
            get
            {
                if (_connection == null) // _connection defined in class -- private static IConnection _connection;
                {
                     _connection = CreateConnection();
                }
                return _connection;
            }
        }

        private IConnection CreateConnection()
        {
            ConnectionFactory factory = new ConnectionFactory()
            {
                HostName = "RabbitMqHostName",
                UserName = "RabbitMqUserName",
                Password = "RabbitMqPassword",
            };

            // why do we need to set this explicitly? shouldn't this be the default?
            factory.AutomaticRecoveryEnabled = true;

            // what is a good value to use?
            factory.NetworkRecoveryInterval = TimeSpan.FromSeconds(5); 

            // what is a good value to use? How is this different from NetworkRecoveryInterval?
            factory.RequestedHeartbeat = 5; 

            IConnection connection = factory.CreateConnection();
            return connection;
        }

RabbitMQ features RabbitMQ 特性

The documentation on RabbitMQ's site is actually really good. RabbitMQ 网站上文档实际上非常好。 If you want to recover queues, exchanges and consumers, you're looking for topology recovery , which is enabled by default.如果您想恢复队列、交换器和消费者,您正在寻找默认启用的拓扑恢复 Automatic Recovery (which is enabled by default ) includes:自动恢复( 默认启用)包括:

  • Reconnect重新连接
  • Restore connection listeners恢复连接侦听器
  • Re-open channels重新开放渠道
  • Restore channel listeners恢复频道侦听器
  • Restore channel basic.qos setting, publisher confirms and transaction settings恢复通道basic.qos设置、发布者确认和交易设置

The NetworkRecoveryInterval is the amount of time before a retry on an automatic recovery is performed (defaults to 5s). NetworkRecoveryInterval是执行自动恢复重试之前的时间量(默认为 5 秒)。

Heartbeat has another purpose, namely to identify dead TCP connections. Heartbeat 还有一个目的,即识别死 TCP 连接。 There are more to read about that at RabbitMQ's site.在 RabbitMQ 的站点上可以阅读更多相关内容

Code sample代码示例

Writing reliable code for recovery is tricky.编写可靠的恢复代码很棘手。 The EndOfStreamException is (as you suspect) most likely due to network problems. EndOfStreamException是(如您所怀疑的)最有可能是由于网络问题。 If you use the management plugin , you can reproduce this by closing the connection from there and see that the exception is triggered.如果您使用管理插件,您可以通过从那里关闭连接来重现这一点,并查看是否触发了异常。 For production-like applications, you might want to have a set of brokers that you alternate between in case of connection failure.对于类似生产的应用程序,您可能希望拥有一组代理,以便在连接失败时交替使用。 If you have several RabbitMQ brokers, you might also want to guard yourself against long-term server failure on one or more of the servers.如果您有多个 RabbitMQ 代理,您可能还想保护自己免受一台或多台服务器上的长期服务器故障的影响。 You might want to implement error strategies, like requeuing the message, or using a dead letter exchange.您可能想要实施错误策略,例如将消息重新排队,或使用死信交换。

I've been thinking a bit of these things and written a thin client, RawRabbit , that handles some of these things.我一直在思考这些事情,并编写了一个瘦客户端RawRabbit来处理其中的一些事情。 Maybe it could be something for you?也许它可能对你有用? If not, I would suggest that you change the QueueingBasicConsumer to an EventingBasicConsumer .如果没有,我建议您将QueueingBasicConsumer更改为EventingBasicConsumer It is event driven, rather than thread blocking.它是事件驱动的,而不是线程阻塞。

var eventConsumer = new EventingBasicConsumer(channel);
eventConsumer.Received += (sender, args) =>
{
    var body = args.Body;
    eventConsumer.Model.BasicAck(args.DeliveryTag, false);
};
channel.BasicConsume(queue, false, eventConsumer);

If you have topology recovery activated, the consumer will be restored by the RabbitMQ Client and start receiving messages again.如果您激活了拓扑恢复,则消费者将被 RabbitMQ 客户端恢复并重新开始接收消息。 For more granular control, hook up event handlers for ConsumerCancelled and Shutdown to detect connectivity problems and Registered to know when the consumer can be used again.对于更精细的控制,为ConsumerCancelledShutdown连接事件处理程序以检测连接问题并Registered以了解何时可以再次使用消费者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM