C#RabbitMQ等待指定超时的一条消息?

时间:2017-05-22 10:11:03

标签: c# rabbitmq

RabbitMQ Wait for a message with a timeoutWait for a single RabbitMQ message with a timeout中的解决方案似乎无法正常工作,因为官方C#库中没有下一个交付方法,并且QueueingBasicConsumer已被删除,因此它只会在任何地方抛出NotSupportedException。

如何从队列中等待指定超时的单个消息?

PS

可以通过Basic.Get()完成,是的,但是,在指定的时间间隔(超额流量,超额CPU)中提取消息是不好的解决方案。

更新

通过implmenetation

EventingBasicConsumer 不支持立即取消。即使您在某个时刻调用 BasicCancel ,即使您通过 BasicQos 指定预取 - 它仍然会在 Frames 中获取,并且这些帧可以包含多个消息。因此,单个任务执行并不好。不要打扰 - 它只是不能处理单个消息。

1 个答案:

答案 0 :(得分:7)

有很多方法可以做到这一点。例如,您可以将EventingBasicConsumerManualResetEvent一起使用,就像这样(仅用于演示目的 - 更好地使用以下方法之一):

var factory = new ConnectionFactory();
using (var connection = factory.CreateConnection()) {
    using (var channel = connection.CreateModel()) {
        // setup signal
        using (var signal = new ManualResetEvent(false)) {
            var consumer = new EventingBasicConsumer(channel);
            byte[] messageBody = null;                        
            consumer.Received += (sender, args) => {
                messageBody = args.Body;
                // process your message or store for later
                // set signal
                signal.Set();
            };               
            // start consuming
            channel.BasicConsume("your.queue", false, consumer);
            // wait until message is received or timeout reached
            bool timeout = !signal.WaitOne(TimeSpan.FromSeconds(10));
            // cancel subscription
            channel.BasicCancel(consumer.ConsumerTag);
            if (timeout) {
                // timeout reached - do what you need in this case
                throw new Exception("timeout");
            }

            // at this point messageBody is received
        }
    }
}

正如您在评论中所述 - 如果您希望同一队列中有多条消息,那么这不是最佳方式。在任何情况下,它都不是最好的方式,我只是为了证明ManualResetEvent的使用,以防案件库本身不提供超时支持。

如果您正在执行RPC(远程过程调用,请求 - 回复) - 您可以在服务器端使用SimpleRpcClientSimpleRpcServer。客户端将如下所示:

var client = new SimpleRpcClient(channel, "your.queue");
client.TimeoutMilliseconds = 10 * 1000;
client.TimedOut += (sender, args) => {
    // do something on timeout
};                    
var reply = client.Call(myMessage); // will return reply or null if timeout reached

更简单的方法:使用基本的Subscription类(它在内部使用相同的EventingBasicConsumer,但支持超时,因此您不需要自己实现),如下所示:

var sub = new Subscription(channel, "your.queue");
BasicDeliverEventArgs reply;
if (!sub.Next(10 * 1000, out reply)) {
     // timeout
}