Rabbit MQ句柄取消令牌

时间:2017-02-06 17:03:49

标签: c# .net rabbitmq

我正在关注这篇文章(http://www.jarloo.com/listening-to-rabbitmq-events/)以消费控制台应用程序上的消息,目前我担心当用户按下CTRL + C并退出应用程序时会发生什么。

至少我希望它在退出程序之前完成处理当前消息和确认。我很困惑如何实现这段代码,因为我是RabbitMQ的新手。

我理解channel.BasicConsume(queueName,true,consumer);阻止线程。

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:0)

对于您试图解决的问题,我只能想到将其视为一个事务。只有当您收到消息时,完全处理它并发送Ack后才认为事务已完成。

如果您首先处理消息,并且某个消息在Ack之前终止应用程序,它将再次排队,当我们重新启动应用程序时,它将再次处理。

如果您首先Ack然后尝试处理该消息并且某个终止该应用程序,则您将丢失该消息。

所以看起来考虑整个过程,因为事务会使它工作,或者另一个选择是处理相同的消息再次处理。

答案 1 :(得分:0)

这是我设法实现的目标,但不确定它是否是最好的方法还是有改进。我没有使用cancellationToken但还不确定我们是否可以使用它。

在我的控制台上,我得到了预期的结果(见截图)

enter image description here

WriteAllText()

QueueConsumer Class

public abstract class QueueConnection : IDisposable
{
    internal IConnection _connection;
    internal IModel _model;
    internal IBasicProperties _properties;
    internal RabbitMQSettings _settings;
    internal protected object _modelLock = new Object();

    public QueueConnection(IOptions<RabbitMQSettings> queueConfig)
    {
        _settings = queueConfig.Value;
    }

    internal bool CreateModel(string queueName)
    {
        if (string.IsNullOrEmpty(queueName))
        {
            throw new ArgumentException("The queue name has to be specified before.");
        }

        lock (_modelLock)
        {
            if (!IsConnected) Connect();
            if (_model == null || _model.IsClosed)
            {
                _model = _connection.CreateModel();

                // When AutoClose is true, the last channel to close will also cause the connection to close. 
                // If it is set to true before any channel is created, the connection will close then and there.
                _connection.AutoClose = true;

                // Configure the Quality of service for the model. Below is how what each setting means.
                // BasicQos(0="Dont send me a new message untill I’ve finshed",  1= "Send me one message at a time", false ="Apply to this Model only")
                _model.BasicQos(0, 50, false);

                const bool durable = true, queueAutoDelete = false, exclusive = false;

                _model.QueueDeclare(queueName, durable, exclusive, queueAutoDelete, null);
                _properties = RabbitMQProperties.CreateDefaultProperties(_model);
            }
        }

        return true;
    }

    public void Connect()
    {
        var connectionFactory = new ConnectionFactory
        {
            HostName = _settings.HostName,
            UserName = _settings.UserName,
            Password = _settings.Password,
        };


        if (_settings.Port.HasValue) connectionFactory.Port = _settings.Port.Value;
        if (_settings.Heartbeat.HasValue) connectionFactory.RequestedHeartbeat = _settings.Heartbeat.Value;
        if (!string.IsNullOrEmpty(_settings.VirtualHost)) connectionFactory.VirtualHost = _settings.VirtualHost;


        _connection = connectionFactory.CreateConnection();
    }

    public bool IsConnected
    {
        get { return _connection != null && _connection.IsOpen; }
    }

    public object GetConnection()
    {
        return _connection;
    }

    public void Disconnect()
    {
        if (_connection != null) _connection.Dispose();
    }

    void IDisposable.Dispose()
    {
        Disconnect();
    }
}

主类

public class QueueConsumer : QueueConnection, IQueueConsumer
{
    private EventingBasicConsumer consumer;
    public QueueConsumer(IOptions<RabbitMQSettings> queueConfig)
        :base(queueConfig) {}

    public void ReadFromQueue(Action<string, ulong> onDequeue, Action<Exception, ulong> onError)
    {
        ReadFromQueue(onDequeue, onError, _settings.QueueName);
    }

    public void ReadFromQueue(Action<string, ulong> onDequeue, Action<Exception, ulong> onError, string queueName)
    {

        CreateModel(queueName);

        consumer = new EventingBasicConsumer(_model);

        // Receive the messages
        consumer.Received += (o, e) =>
        {
            try
            {
                var queueMessage = Encoding.UTF8.GetString(e.Body);
                onDequeue.Invoke(queueMessage, e.DeliveryTag);
            }
            catch (Exception ex)
            {
                onError.Invoke(ex, e.DeliveryTag);
            }
        };

        // if the consumer shutdown reconnects to rabbitmq and begin reading from the queue again.
        consumer.Shutdown += (o, e) =>
        {
            CreateModel(queueName);
            ReadFromQueue(onDequeue, onError, queueName);
        };

        _model.BasicConsume(queueName, false, consumer);

    }

    public void AcknowledgeMessage(ulong deliveryTag)
    {
        if (!IsConnected) Connect();
        CreateModel(_settings.QueueName);
        _model.BasicAck(deliveryTag, false);
    }

    public void StopListening()
    {
        _model.BasicCancel(consumer.ConsumerTag);
    }
}