RabbitMQ的工作队列
在上一个教程中,我们编写了程序来发送和接收来自命名队列的消息。在这一个中,我们将创建一个工作队列,用于在多个工作人员之间分配耗时的任务。
本次主要介绍的是队列的消息应答、消息公平派遣、消息持久性。
1 消息应答
在我们从队列中取出消息执行任务的时候,可能需要几秒,我们可能会有疑问,如果在我们还在处理这个消息的时候,我们是如何处理的?或者是当消费者取得队列中的消息,队列就把这个消息给删除了,但是如果消费者在取完消息执行任务的时候失败了,那么这个消息也就丢失了;但是实际我们是希望如果如果执行任务的消费者失败了,另外一个消费者还可以继续获得这个消息去执行任务。为了确保消息不被丢失,RabbitMQ支持消息应答。当消费者获得消息任务成功执行后,告诉RabbitMQ,这个消息没用了,可以删除了;RabbitMQ收到消费者应答后,再删除队列中的消息。
如果消费者死机(其通道关闭,连接关闭或TCP连接丢失),而不发送确认信息(不应答),RabbitMQ将会明白消息未被完全处理并重新排队。如果同时有其他消费者在线,则会迅速将其重新提供给另一个消费者。这样就可以确保没有消息丢失,即使工作人员偶尔也会死亡。
消息应答主要是消费者服务中设置,在消费者指定消费队列的时候,打开手动应答机制
// 消费者指定消费队列,打开应答机制,注意false才是打开手动应对,true为自动应答
boolean ack =false;
channel.basicConsume(QUEUE_NAME, ack, consumer);
1.1 消费者完整代码如下
package MQ.WorkQueues.Response;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
@SuppressWarnings("deprecation")
/**
* @Title: MQ.WorkQueues.Worker.java
* @Package MQ.WorkQueues
* @Description:TODO(模拟MQ接收队列消息后手动应答,持久性)
* @Copyright: Copyright (c) 2017 YUANH All Rights Reserved
* @authoryuanh
* @date 2017-5-10下午3:48:59
*/
public classWorkerResponse {
private final static String QUEUE_NAME ="hello";
public static void main(String[] argv)throws Exception {
// 创建连接连接到MabbitMQ
ConnectionFactoryfactory = newConnectionFactory();
// 设置MabbitMQ所在主机ip或者主机名
factory.setHost("127.0.0.1");
factory.setUsername("yuanh");
factory.setPassword("yuanh");
factory.setPort(5672);
factory.setVirtualHost("y_yuanh");
Connectionconnection = factory.newConnection();
Channelchannel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false, false,false, null);
System.out.println(" [*]接收消息等待中,推出请按 CTRL+C");
QueueingConsumerconsumer = newQueueingConsumer(channel);
// 消费者指定消费队列,打开应答机制,注意false才是打开手动应对,true为自动应答
boolean ack =false;
channel.basicConsume(QUEUE_NAME, ack, consumer);
try {
while (true) {
QueueingConsumer.Deliverydelivery = consumer.nextDelivery();
Stringmessage = newString(delivery.getBody());
System.out.println("接收 '" + message +"'");
try {
doWork(message);
}finally{
System.out.println("结束");
// 另外需要在每次处理完成一个消息后,手动发送一次应答(ack=false)。
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),