消费端限流、ACK与重回队列

限流概念

假设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和NACK

消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿

如果由于服务器宕机等严重问题,我们需要手工进行ACK保障消费端消费成功

消费端重回队列

消费端重回队列是为了没有处理成功的消息,把消息重新投递给Broker

在实际应用中,都会关闭autoAck重回队列,也就是将autoAck设置为false

ACK与重回队列消费端代码

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的无法消费,只要遇到就重回队列的尾端,如图

猜你喜欢

转载自blog.csdn.net/lql_h/article/details/88812867