RabbitMQ--工作队列

 

 

1、工作队列:用来将耗时的任务分发给多个消费者。一个消息只能被一个消费者获取。该模式的主要思想是:避免立即执行资源密集型、且必须等待其完成的任务,而是安排稍后完成任务。

2、涉及到的问题:

  2.1、 消息如何分配:多个接收端接收同一个Queue时,采用了Round-robin分配算法,即轮叫调度——依次分配给各个接收方。例如,在有两个 Worker 的情况下,假设所有奇数消息都很庞大、偶数消息都很轻量,那么一个 Worker 将会一直忙碌,而另一个 Worker 几乎不做任何工作。我们可以使用参数设置prefetchCount = 1basicQos方法。这就告诉 RabbitMQ 同一时间不要给一个 Worker 发送多条消息。或者换句话说,不要向一个 Worker 发送新的消息,直到它处理并确认了前一个消息。相反,它会这个消息调度给下一个不忙碌的 Worker。

// 告知 RabbitMQ,在未收到当前 Worker 的消息确认信号时,不再分发给消息,确保公平调度。
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);

 

  2.2、消息确认

    a、为了确保消息永远不会丢失,RabbitMQ 支持 消息确认 机制。消费者回发一个确认信号 Ack(nowledgement) 给 RabbitMQ,告诉它某个消息已经被接收、处理并且可以自由删除它。

    b、如果一个消费者在还没有回发确认信号之前就挂了(其通道关闭,连接关闭或者 TCP 连接丢失),RabbitMQ 会认为该消息未被完全处理,并将其重新排队。

         c、如果有其他消费者同时在线,该消息将会被会迅速重新分发给其他消费者。这样,即便 Worker 意外挂掉,也可以确保消息不会丢失。

              d、没有任何消息会超时;当消费者死亡时,RabbitMQ 将会重新分发消息。即使处理消息需要非常非常长的时间也没关系。

      e、默认开启了消息确认(接收方接收到消息后,立即向服务器发回确认)。消息接收方处理完消息后,向服务器发送消息确认,服务器再删除该消息。对于耗时的work,可以先关闭自动消息确认,在work完成后,再手动发回确认。

channel.BasicConsume(queue: "task_queue", noAck: false,consumer: consumer);
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);

3、持久化:当 RabbitMQ 退出或崩溃时,它会忘记已存在的队列和消息,除非告诉它不要这样做。为了确保消息不会丢失,有两件事是必须的:我们需要将队列和消息标记为持久。

// 声明队列,标记为持久性。durable:true
channel.QueueDeclare(queue: "task_queue", durable: true,exclusive: false, autoDelete: false,arguments: null);

 // 将消息标记为持久性。
var properties = channel.CreateBasicProperties();
properties.Persistent = true;

4.代码:

  send:

 public Send() {
          
            var factory = new ConnectionFactory() { HostName = "localhost" };


            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                // 声明队列,标记为持久性。durable:true
                channel.QueueDeclare(queue: "task_queue",
                                     durable: true,
                                     exclusive: false,
                                     autoDelete: false,
                                     arguments: null);



                // 将消息标记为持久性。
                var properties = channel.CreateBasicProperties();
                properties.Persistent = true;
                for (var i = 0; i < 50; i++)
                {
                    var message = $"{i + 1}helloworld{DateTime.Now.ToString()}";
                    var body = Encoding.UTF8.GetBytes(message);
                    channel.BasicPublish(exchange: "",
                                     routingKey: "task_queue",
                                     basicProperties: properties,
                                     body: body);
                    Console.WriteLine(" Sender Sent {0}", message);
                }

            }
        }

 

 

  receive:

  

public Receive() {
           
            var factory = new ConnectionFactory() { HostName = "localhost" };

           
            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                // 声明队列,标记为持久性。durable:true
                channel.QueueDeclare(queue: "task_queue",
                                     durable: true,
                                     exclusive: false,
                                     autoDelete: false,
                                     arguments: null);

                // 告知 RabbitMQ,在未收到当前 Worker 的消息确认信号时,不再分发给消息,确保公平调度。
                channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);

                Console.WriteLine(" [*] Waiting for messages.");

                // 构建消费者实例。
                var consumer = new EventingBasicConsumer(channel);

                // 绑定消息接收事件。
                consumer.Received += (model, ea) =>
                {
                    var body = ea.Body;
                    var message = Encoding.UTF8.GetString(body);
                    Console.WriteLine(" [x] Received {0}", message);

                    // 模拟耗时操作。
                    int dots = message.Split('.').Length - 1;
                    Thread.Sleep(3000);

                    Console.WriteLine(" [x] Done");

                    // 手动发送消息确认信号。
                    channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
                };

                channel.BasicConsume(queue: "task_queue",
                                     noAck: false,
                                     consumer: consumer);

                Console.ReadLine();
            }
        }

 

posted @ 2020-05-12 17:44  汪汪汪~~  阅读(182)  评论(0编辑  收藏  举报