RabbitMQ,是否可以从通道中消耗1条消息,同时保留FIFO?

时间:2016-03-31 11:47:34

标签: rabbitmq message-queue messagebroker

我使用RabbitMQ和C#作为消息代理框架。 在我的场景中,我有许多消费者可以从不同的队列消费。 从消费者的角度来看,几个队列构成了一个逻辑组。

例如: 队列1,队列2,队列3。

Consumer1将希望从[Queue1,Queue3]获取第一条消息,并且只有在不可用的情况下尝试从Queue2获取

Consumer2将希望从[Queue2,Queue3]中获取第一条消息,并且只有在不可用时尝试从Queue1获取

下面是一个可能的设置,每个消费者关心队列的子集。 Consumer2对所有队列感兴趣,优先级为1,2,因此消费者维护优先级逻辑。 请注意,这与此处显示的工作队列不同[{3}} RabbitMQ tutorials

这是一个应该返回消息的类:

public class MessagesProvider
{

    private IConnection _connection;
    private IModel _channel;
    private readonly IConnectionFactory _connectionFactory;
    private ConcurrentDictionary<string, string> _tagsQueueItems;
    private EventingBasicConsumer _consumer;
    private CancellationTokenSource _cts;
    private TaskCompletionSource<BasicGetResult> _tcs;

    public MessagesProvider()
    {
        _connectionFactory = new ConnectionFactory()
        {
            HostName = "localhost"
        };

    }

    public Task<BasicGetResult> GetMessage(int timeout, IEnumerable<string> queues)
    {
        _tagsQueueItems = new ConcurrentDictionary<string, string>();
        _cts = new CancellationTokenSource();
        _connection = _connectionFactory.CreateConnection();
        _channel = _connection.CreateModel();
        _channel.SingleMessagePerChannel();
        _consumer = new EventingBasicConsumer(_channel);
        _consumer.Received += OnReceive;

        foreach (var queue in queues)
        {
            var tag = _channel.BasicConsume(queue, false, _consumer);
            _tagsQueueItems.AddOrUpdate(queue, tag, (k, o) => queue);
        }

        return _tcs.Task;
    }

    private void SetResult(BasicGetResult result)
    {
        _tcs.SetResult(result);
        if (_channel.IsOpen)
            _channel.Close();
        if (_connection.IsOpen)
            _connection.Close();
    }

    private void OnReceive(object sender, BasicDeliverEventArgs e)
    {
        _consumer.Received -= OnReceive;
        _channel.BasicAck(e.DeliveryTag, false);
        var result = new BasicGetResult(e.DeliveryTag, e.Redelivered, e.Exchange, e.RoutingKey, 1,
            e.BasicProperties, e.Body);

        SetResult(result);

    }
}

频道定义:

 public static class  ChannelExtension
{
    public static void SingleMessagePerChannel(this IModel channel)
    {
        channel.BasicQos(0,1,true);
    }

    public static void SingleMessagePerConsumer(this IModel channel)
    {
        channel.BasicQos(0,1,false);
    }
}

问题是我正在逐个注册队列(在方法“GetMessage”中),我无法找到一个“原子”操作,它会从一组队列中带来下一个FIFO消息。

我正在寻找一种方法: _consumer.getNextMessage();

另一种方法将使用来自第一组优先级的所有第一大多数消息,在消费者端过滤它们以查找最旧的消息并且不发送其他消息的确认(在同一优先组)。 这种方法也存在问题,因为它意味着在获取消息时,其他消费者将无法处理它们(直到没有确认)。

有什么建议吗?感谢。

1 个答案:

答案 0 :(得分:0)

I don't think you're using Queues and Consumers in the way they were designed. Think of a queue as a bucket of work to be done. Each bucket of work can have N consumers that do the work.

Each consumer should be designed to work with a single queue and not know or care about other queues or other consumers.

If you're trying to achieve a "high priority" queue then I suggest you have a single exchange with 2 different queues:

  • Regular Priority Queue
  • High Priority Queue

Then, by default, messages get put into the regular priority queue. When a message comes along that needs to be high priority you can set the header on the message so that it gets pushed into the High Priority Queue.

We use this same approach today using a direct queue and adding a header when we want a message to go into the high priority queue.