什么是在RabbitMQ的C#客户端中优雅地停止消息消费的优雅方法?

时间:2014-09-29 17:07:30

标签: c# rabbitmq messaging amqp consumer

我正在设置一个标准的独立线程,在C#中监听RabbitMQ。假设在线程中侦听的方法如下所示:

public void Listen()
{
    using (var channel = connection.CreateModel())
    {
        var consumer = SetupQueues(channel);
        while (true)
        {
            var ea = consumer.Queue.Dequeue();    // blocking call
            handler.HandleMessage(channel, ea);
        }
    }
}

在RabbitMQ的C#客户端中优雅地停止消息消息的优雅方法是什么?请记住,我在RabbitMQ示例/文档或这些SO问题中没有发现任何用处:

此处的问题是consumer.Queue.Dequeue()是阻止来电。我尝试过这些选项:

  • 致电channel.BasicCancel(string tag)。这会在阻止调用中导致System.IO.EndOfStreamException。出于显而易见的原因,我不想将此异常用作控制流程的一部分。

  • 调用consumer.Queue.Dequeue(int millisecondsTimeout, out T result)并在循环迭代之间检查标志。这可以起作用,但看起来很糟糕。

我想让线程优雅地退出并清理我可能拥有的任何非托管资源,因此没有线程中止等等。

感谢任何帮助。感谢

2 个答案:

答案 0 :(得分:1)

DeQueue的超时&国旗是这样做的方式。它是一种非常常见的模式,也就是为什么许多阻塞调用都提供了启用超时的版本。

或者,抛出(已知的)异常并不一定是控制流的坏事。优雅地关闭可能意味着实际捕获异常,评论"当请求通道关闭"然后干净地返回时抛出此异常。这就是TPL的一部分如何与CancellationToken一起使用。

答案 1 :(得分:1)

阻止方法不是属性事件驱动的。 我不建议他们使用consumer.Queue.Dequeue();

无论如何,我通常不使用consumer.Queue.Dequeue();

我以这种方式扩展默认消费者:

class MyConsumer : DefaultBasicConsumer {

  public MyConsumer(IModel model):base(model)
  {

  }
  public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body) {
    var message = Encoding.UTF8.GetString(body);
    Console.WriteLine(" [x] Received {0}", message);
  }
}


class Program
{
  static void Main(string[] args)
  {

    var factory = new ConnectionFactory() { Uri = "amqp://aa:bbb@lemur.cloudamqp.com/xxx" };
    using (var connection = factory.CreateConnection())
    {
      using (var channel = connection.CreateModel())
      {
      channel.QueueDeclare("hello", false, false, false, null);
      var consumer = new MyConsumer(channel);
      String tag = channel.BasicConsume("hello", true, consumer);
      Console.WriteLine(" [*] Waiting for messages." +
                               " any key to exit");
      Console.ReadLine();
      channel.BasicCancel(tag);


        /*while (true)
        {
          /////// DON'T USE THIS   
          var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
          var body = ea.Body;
          var message = Encoding.UTF8.GetString(body);
          Console.WriteLine(" [x] Received {0}", message);
        }*/
      }
    }


  }
}

通过这种方式,您没有阻止方法,并且可以正确释放所有资源。

修改

我认为使用 ctrl + C 打破程序总是错误的。