如何实现RabbitMQ的订阅模型并重新排队未处理的消息

时间:2017-03-22 15:55:54

标签: c# rabbitmq

我有一个Windows服务,它使用来自远程托管的RabbitMQ队列的消息。

我有工作代码,它以固定的时间间隔唤醒并连接到队列并接收消息并处理它们(大多数将它们保存到数据库中)。这是对我有用的简化版本:

        #region currentmethod

        IConnection connection = factory.CreateConnection();
        IModel channel = connection.CreateModel();
        channel.ExchangeDeclare(EXCHANGE_NAME, ExchangeType.Direct, true, false, null);
        QueueDeclareOk queueDeclareOK = channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: true, arguments: null);
        channel.QueueBind(queue: queueName, exchange: EXCHANGE_NAME, routingKey: routingkey);

        Console.WriteLine(queueDeclareOK.MessageCount.ToString());

        BasicGetResult result = channel.BasicGet(queueName, false);
        byte[] body = result.Body;
        string message = Encoding.UTF8.GetString(body);
        if (ProcessData(message))
            channel.BasicAck(result.DeliveryTag, false);
        else
            channel.BasicNack(result.DeliveryTag, false, true);

        channel.Close(200, "goodbye");
        connection.Close();
        #endregion currentmethod

我想要做的是实现https://www.rabbitmq.com/tutorials/tutorial-three-dotnet.html所述的发布 - 订阅模型,以便当消息进入队列时,订阅者立即处理它们。我需要确保的是,如果消息未成功处理,它将保留在队列中并且不会丢失。

以下是基本上这些代码的代码:

        #region subscribeModel
        using (connection = factory.CreateConnection())
        {
            using (channel = connection.CreateModel())
            {
                channel.ExchangeDeclare(EXCHANGE_NAME, ExchangeType.Direct, true, false, null);
                channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: true, arguments: null);  
                channel.QueueBind(queueName, EXCHANGE_NAME, routingKey);

                EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
                consumer.Received += (o, e) =>
                    {
                        string data = Encoding.ASCII.GetString(e.Body);
                        Console.WriteLine(data);                           
                    };

                string consumerTag = channel.BasicConsume(queueName, false, consumer);                 

                Console.WriteLine("Listening, press ENTER to quit");
                Console.ReadLine();                                        
            }
        }

        #endregion subscribeModel

我有三个困难。

  1. 读取消息后,它们处于Unacked状态,但我需要它们返回Ready状态。如果它们被成功处理,它们应该是Acked并离开队列,但如果处理失败,它们必须保留在队列中。

  2. 我需要做一些事情而不是将数据写入控制台,但是我将Process代码放在这个模型中的哪个位置?

  3. 当我的代码在订阅模型中终止时,我连接的队列将被删除,丢失任何状态下的任何消息。

  4. 有人知道如何重新排队未处理的邮件,并防止在我的消费者关闭时删除队列吗?

1 个答案:

答案 0 :(得分:1)

我在上周没有机会看到这么多,但今天做了更多的研究,我想我现在可以回答我自己的问题,希望在此过程中帮助其他人。

  1. 使用订阅模型时将消息返回Ready状态是没有意义的,因为这将阻止队列的其余部分。订户将继续尝试处理未包装的消息,直到将其从队列中删除。来自:https://www.rabbitmq.com/confirms.html
  2.   

    肯定的确认只是指示RabbitMQ记录一条消息   交付。 basic.reject的否定确认有   同样的效果。差异主要在于语义:积极   确认假设消息已成功处理   他们的负面对应物表明交付没有得到处理   但仍应删除。

    我可以通过将有缺陷的消息发送到新队列来实现我需要做的事情,在那里可以对它们进行不同的处理以解决使它们有缺陷的任何问题,如下所示:

                using (channel)
                {
    
                    EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
                    consumer.Received += (o, e) =>
                    {
                        string data = Encoding.ASCII.GetString(e.Body);
                        result = MQ.utilities.Utilities.ProcessData(data, counter);
                        if (result)
                            channel.BasicAck(e.DeliveryTag, false);
                        else
                        {
                            channel.BasicNack(e.DeliveryTag, false, false);
                            //send message to another queue ...
                            IBasicProperties basicProperties = channel.CreateBasicProperties();
                            channel.BasicPublish(_exchangeName, "newqueue", basicProperties, e.Body);
                        }
                    };
    
                    string consumerTag = channel.BasicConsume(_publishSubscribeQueueOne, false, consumer);
    
                    Console.WriteLine("Listening, press ENTER to quit");
                    Console.ReadLine();
                }  
    
    1. 可以在消费者中收到的事件上发生procesdata方法。

    2. 这是因为队列被声明为autodelete。如果未设置为自动解除队列,则当消费者终止时,未打包的消息将返回到Ready状态。