假设RabbitMQ服务器有上万条未处理的消息,我们随便打开一个消费者客户端,会出现下面情况:
巨量的消息瞬间全部推送过来,但是单个客户端无法同时处理这么多数据
RabbitMQ提供了一种qos(服务质量保证)功能,即在非自动确认消息的前提下,如果一定数据的消息(通过基于consume或者channel设置qos的值)未被确认前,不进行消费新的消息
void BasicQos(unit prefetchSize,ushort prefetchCount,bool global); //消费端体现
prefetchSize:限制消息的大小,一般为0,不做消息的限制
prefetchCount:告诉RabbitMQ不要同时给一个消费者推送多于N个消息,即一旦有N个消息还没有ack,则该consumer将block掉,直到有消息ack,N一般设置为1
global:是否将上面的设置应用于channel,一般设为false(consume),true为channel
public class Consumer {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.17.17");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String exchangeName = "test_qos_exchange";
String queueName = "test_qos_queue";
String routingKey = "qos.#";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//1 限流方式 第一件事就是 autoAck设置为 false,basicConsume的第二个参数
channel.basicQos(0, 1, false);
channel.basicConsume(queueName, false, new MyConsumer(channel));
}
}
public class MyConsumer extends DefaultConsumer {
private Channel channel ;
public MyConsumer(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("consumerTag: " + consumerTag);
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
channel.basicAck(envelope.getDeliveryTag(), false);
//long deliveryTag, delivery tag是单调递增的正整数
//boolean multiple 是否批量签收,因为设置是1个,所以不批量签收
}
}
channel.basicQos(0, 1, false);
channel.basicConsume(queueName, false, new MyConsumer(channel));
//限流方式 第一件事就是 autoAck设置为 false,basicConsume的第二个参数
channel.basicAck(envelope.getDeliveryTag(), false);
//long deliveryTag, delivery tag是单调递增的正整数
//boolean multiple 是否批量签收,因为设置是1个,所以不批量签收
如果屏蔽channel.basicAck(envelope.getDeliveryTag(), false),则未确认无法进行消费,如图
加上channel.basicAck(envelope.getDeliveryTag(), false)则正常消费
消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿
如果由于服务器宕机等严重问题,我们需要手工进行ACK保障消费端消费成功
消费端重回队列是为了没有处理成功的消息,把消息重新投递给Broker
在实际应用中,都会关闭autoAck重回队列,也就是将autoAck设置为false
public class Consumer {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.17.17");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String exchangeName = "test_ack_exchange";
String queueName = "test_ack_queue";
String routingKey = "ack.#";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
// 手工签收 必须要关闭 autoAck = false
channel.basicConsume(queueName, false, new MyConsumer(channel));
}
}
public class MyConsumer extends DefaultConsumer {
private Channel channel ;
public MyConsumer(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("body: " + new String(body));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if((Integer)properties.getHeaders().get("num") == 0) {
channel.basicNack(envelope.getDeliveryTag(), false, true);
//DeliveryTag
//是否支持批量 单条 false
//是否重回队列的尾端
} else {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
}
num=0的无法消费,只要遇到就重回队列的尾端,如图