重置OnMessageReceived后,RabbitMQ不检索消息

时间:2019-02-27 19:54:05

标签: c# rabbitmq

我正在使用RabbitMQ.Client(C#)与RabbitMQ一起工作。一旦删除并重新添加已接收消息的事件处理程序,我将无法从队列中检索消息。 consumer.Received -= OnMessageRecieved; 我有一个复杂的系统,其中Windows服务订阅RabbitMQ队列并处理消息。有多个线程正在运行,用于执行各种操作-用于调用PUSH api的计时器,用于执行api身份验证的另一个计时器,等等。如果api身份验证失败,我们不想处理来自队列的消息。我们要保持消息处于就绪状态。仅当api身份验证成功时,我们要处理消息。因此,在失败事件上,我们删除事件处理程序,在成功时,我们将其添加回去。当我们这样做时,将成功添加事件处理程序,但是现在不会从队列中检索消息。

为了模拟这一点,我创建了一个控制台应用程序。我已经在不到一个小时的时间内编写了这段代码,我知道这段代码非常粗糙,请原谅。 sub.StopReceiveMessages();具有删除处理程序consumer.Received -= OnMessageRecieved的代码。并且,sub.StartReceiveMessages();具有删除处理程序consumer.Received += OnMessageRecieved的代码。当您重新添加它时,我认为它会正常工作。但是,它不再符合MessageReceived()。尽管我使用相同的使用者,是否必须再次调用BasicConsume?任何帮助将不胜感激。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Client = RabbitMQ.Client;

namespace RabbitMQTest
{
    class Program
    {


        static void Main(string[] args)
        {
            MessageBusSubscription sub = new MessageBusSubscription();

            sub.Subscription("EmployeeDataChanged", "HR", "CompanyA", 5, 5000);
            sub.MessagesReceived += MessageReceived;
            Console.WriteLine("Press ESC to exit");
            while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
            {
                // Simulating an event where we have to stop pulling the messages from the queue
                sub.StopReceiveMessages();

                Thread.Sleep(2000);

                // After some time, the issue is resolved and now we resume reading the messages from the queue
                sub.StartReceiveMessages();
            }
            sub.Dispose();
            Environment.Exit(0);
        }

        private static bool MessageReceived(string topic, string subscription, List<MessageContainer> messages)
        {
            List<MessageContainer> data = null;
            data = messages as List<MessageContainer>;

            foreach (var messageContainer in data)
            {
                // Do something with the message
                // Ack or Reject based on some logic
            }

            return true;
        }
    }
    public class MessageBusSubscription : IDisposable
    {
        #region variables
        Client.Events.EventingBasicConsumer consumer;
        Client.ConnectionFactory factory;        
        private System.Timers.Timer _timer = null;
        private Client.IModel _channel = null;
        private string _topic = string.Empty;
        private string _subscription = string.Empty;
        int batchCounter = 0;
        int batchSize = 0;
        ManualResetEvent _waitHandle = new ManualResetEvent(false);
        bool _disposing = false;
        bool _isSubscribed = false;
        List<MessageContainer> messages = new List<MessageContainer>();
        private object _processMessageLocker = new object();
        public event Func<string, string, List<MessageContainer>, bool> MessagesReceived;
        #endregion
        public MessageBusSubscription()
        {
            Client.IConnection conn = GetConnection();
            _channel = conn.CreateModel();
        }
        public void Subscription(string exchangeName, string queueName, string routingKey, int batchSize, double batchInterval)
        {
            _topic = exchangeName;
            _subscription = queueName;
            DeclareExchangeAndQueue(exchangeName, queueName, routingKey);

            if (batchInterval > 0 && batchSize > 1)
            {
                _timer = new System.Timers.Timer(batchInterval);

                _timer.Elapsed += (o, e) => {
                    ProcessMessagesReceived(exchangeName, queueName, true);
                };
            }
            Subscribe(routingKey, exchangeName, queueName, batchSize, batchInterval);
        }
        public Task Subscribe(string routingKey, string topic, string subscription, int _batchSize, double batchInterval)
        {
            try
            {
                consumer = new Client.Events.EventingBasicConsumer(_channel);

                batchCounter = 0;
                batchSize = _batchSize;

                consumer.Received += OnMessageRecieved;
                _isSubscribed = true;

                //RabbitMQ PUSH implementation using RabbitMQ.Client library
                var t = Task.Factory.StartNew(() =>
                {
                    try
                    {
                        if (_timer != null)
                        {
                            _timer.Start();
                        }

                        var queueName = string.Join(".", routingKey, topic, subscription);

                        if (!_disposing)
                        {
                            _channel.BasicConsume(queueName, false, consumer);
                            _waitHandle.WaitOne();
                        }

                        if (_timer != null)
                        {
                            _timer.Stop();
                            _timer.Dispose();
                        }

                        if (_channel != null)
                        {
                            if (_channel.IsOpen)
                                _channel.Close();
                            _channel.Dispose();
                        }
                    }
                    catch (Exception ex)
                    {

                    }

                });

                return t;
            }
            catch (Exception ex)
            {
                var exTask = new Task(() => { throw new AggregateException(ex); });
                exTask.RunSynchronously();
                return exTask;
            }
        }
        public void OnMessageRecieved(Client.IBasicConsumer sender, Client.Events.BasicDeliverEventArgs e)
        {
            try
            {
                string sourceExchange = string.Empty;
                string sourceQueue = string.Empty;

                string body = Encoding.ASCII.GetString(e.Body);
                string routingKey = e.RoutingKey;
                ulong deliveryTag = e.DeliveryTag;
                sourceExchange = "";
                sourceQueue = "";
                MessageContainer msgContainer = new MessageContainer();
                msgContainer.Message = body;

                batchCounter++;
                msgContainer.DeliveryTag = deliveryTag;

                lock (_processMessageLocker)
                {
                    messages.Add(msgContainer);
                    ProcessMessagesReceived(_topic, _subscription, false);
                }
            }
            catch (Exception ex)
            {

            }

        }
        public void ProcessMessagesReceived(string topic, string subscription, bool hasTimerElapsed)
        {
            try
            {
                // if it's the last message in the batch, or the interval has elapsed
                if ((batchCounter % batchSize == 0 && messages.Count > 0) || (hasTimerElapsed && messages.Count > 0))
                {
                    if (_timer != null)
                    {
                        _timer.Stop();
                    }

                    lock (_processMessageLocker)
                    {
                        // process the message
                        if (!MessagesReceived(topic, subscription, messages))
                        {
                            throw new Exception("Message processing exception - look in the default error queue (broker)");
                        }
                        messages.Clear();
                    }

                    batchCounter = 0;
                    if (_timer != null)
                    {
                        _timer.Start();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        public Client.IConnection GetConnection()
        {
            factory = new Client.ConnectionFactory();

            factory.UserName = "guest";
            factory.Password = "guest";
            factory.VirtualHost = "/";
            factory.HostName = "localhost";
            factory.Protocol = Client.Protocols.AMQP_0_9_1;
            factory.Port = 5673;

            return factory.CreateConnection();
        }
        public void DeclareExchangeAndQueue(string exchangeName, string queueName, string routingKey)
        {
            using (var exchangeConn = factory.CreateConnection())
            using (Client.IModel channel = exchangeConn.CreateModel())
            {
                channel.ExchangeDeclare(exchangeName, Client.ExchangeType.Direct);

                var queue = String.Join(".", routingKey, exchangeName, queueName);
                channel.QueueDeclare(queue, false, false, false, null);
                channel.QueueBind(queue, exchangeName, routingKey, null);
            }
        }
        public void StartReceiveMessages()
        {
            if (_timer != null && !_isSubscribed)
            {
                _timer.Start();
            }

            if (consumer != null && !_isSubscribed)
            {
                consumer.Received += OnMessageRecieved;
                _isSubscribed = true;
            }            
        }
        public void StopReceiveMessages()
        {
            if (_timer != null)
            {
                _timer.Stop();
            }

            if (consumer != null)
            {
                consumer.Received -= OnMessageRecieved;
                _isSubscribed = false;
            }                        
        }
        public void Dispose()
        {
            _disposing = true;
            _waitHandle.Set();
            _waitHandle?.Dispose();
            _waitHandle = null;

            if (_timer != null)
            {
                _timer.Stop();
                _timer.Dispose();
            }
        }
    }
    public class MessageContainer
    {
        public ulong DeliveryTag { get; set; }
        public string Message { get; set; }
    }
}

1 个答案:

答案 0 :(得分:0)

不要取消订阅Received事件,而是使用BasicCancel方法停止使用邮件,然后再次使用BasicConsume开始使用。

许多线程同步代码以及在Task中运行使用者并非最佳实践。如果您希望获得有关此代码的进一步帮助,请将其保存在git存储库或gist中的某个位置,并在官方邮件列表中进行后续操作。


注意: RabbitMQ团队监视rabbitmq-users mailing list,并且有时仅在StackOverflow上回答问题。