rabbitmq消息确认
消息确认
当生产者投递消息到broker,rabbitmq把消息分发到消费者。 如果设置了autoAck=true 消费者会自动确认收到信息。这时broker会立即将消息删除,这种情况下如果消费者出现异常(连接中断)该消息就会丢失。为了保证消息能够被正确的消费,rabbitmq支持消息确认。
String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
sender:
public class Send {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!kkkkkkkkkkkkkk";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
receive:
public class Receive {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// channel.basicQos(1);//使得每个Consumer在同一个时间点最多处理一个Message。在接收到该Consumer的ack前,不会将新的Message分发给它
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties,byte[] body) throws IOException {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
// channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
启动sender 发送3消息:
rabbitmq 中有了两条消息记录:
启动消费者:
channel.basicConsume(QUEUE_NAME, false, consumer);
消费者没有确认消息被消费,消息一直留在队列中,只有当从有新的消费者加入时,消息被分发到新的消费者。如果由于连接中断,消费者退出时,那么消息会被轮训分发到其余的消费者。
公平分发
rabbitmq 是以轮训的方式进行分发消息,将N个消息发到n个消费者,这样有可能出现某些消费者要处理一些耗时的消息堆积在那里,而有些消费者处理很简单的消息,无事可做。为了我解决这个问题我们可以使用:
channel.basicQos(1)
关于消息确认的疑问
“ACK机制可以保证消费者如果拿了队列的消息,处理出错了,那么队列中还有这个消息,仍然可以给下个机子来跑。但是,个人觉得一般处理消息出错都是因为代码逻辑或者出bug,即使 队列中后来仍然保留该消息,然后再给某一个消费者消费,不还是报错吗?
Ps:当然,如果一个机子宕掉,消息还有,还可以给另外的机子用,这种情景下 ACK 是很有用的。但是个人觉得这种应该是少数情况吧。”
官方介绍:
https://www.rabbitmq.com/confirms.html
consumer 做了一个ACK是为了告诉broker该条消息已经被消费,broker如果没有收到acknowledgment 会一直保存该信息,不会分发给其他的consumer,指导当前的consumer 发生异常断开连接 broker 才会将该条消息发送给其他的consumer.所以你的 consumer 代码必须能够处理各种异常,确保只要收到一条消息,最终一定能够执行一条 ACK / NACK。
它使得每个Consumer在同一个时间点最多处理一个Message。在接收到该Consumer的ack前,不会将新的Message分发给它。这样就以保证等消费者处理完数据之后才会发送改消息给该消费者。但是这样也可能会使得所有的消息积压在rabbitmq中。