RabbitMQ:消息拒绝的介绍和使用

1.声明

当前内容用于本人学习和复习之用,当前内容主要为消息拒绝的测试和使用

当前内容来源:RabbitMQ中的消息拒绝

2.官方的Rejecting Messages和Negative Acknowledgements介绍

When a consumer application receives a message, processing of that message may or may not succeed. An application can indicate to the broker that message processing has failed (or cannot be accomplished at the time) by rejecting a message. When rejecting a message, an application can ask the broker to discard or requeue it. When there is only one consumer on a queue, make sure you do not create infinite message delivery loops by rejecting and requeueing a message from the same consumer over and over again.

当一个消费者程序接收了一个消息,处理消息可能成功也可能不成功,一个程序可以通过拒绝的方式向broker表示消息处理失败的(或者无法完成),当拒绝一个消息,这个程序将告诉broker是否要将这个拒绝的消息重新入队。当只有一个消费者在队列上的时候,小心出现死循环方式

Messages are rejected with the basic.reject method. There is one limitation that basic.reject has: there is no way to reject multiple messages as you can do with acknowledgements. However, if you are using RabbitMQ, then there is a solution. RabbitMQ provides an AMQP 0-9-1 extension known as negative acknowledgements or nacks. For more information, please refer to the Confirmations and basic.nack extension guides.

消息拒绝使用basic.reject方法,这个basic.reject有一个缺点:就是不能不能像确认一样自动的拒绝多条消息。当然如果你使用RabbitMQ,那么这里有一个解决方案。RabbitMQ提供了一个AMQP 0-9-1的扩展,作为是否确认或者否定。需要更多的消息请参考basic.nack扩展向导

通过上面分析发现拒绝的使用方式

  1. 直接使用basic.reject方法实现拒绝,可以携带参数用确认是否将拒绝的消息重新如队列
  2. 如果只有一个消费者则使用拒绝的时候容易出现死循环
  3. 拒绝消息具有一个缺点,它必须一个一个的拒绝,不能像消息确认一样可以多条确认
  4. RabbitMQ提供了一个插件来解决这个问题:basic.nack

3.测试拒绝消息

1.创建两个消费者,一个执行消费,一个模拟异常拒绝消费消息,并把消息重新放入队列中

/**
 * @description 测试消息拒绝(使用basic.reject拒绝消息)
 * @author hy
 * @date 2020-05-14
 */
public class MessageReject1Test {
	private static final String queueName = "hello";

	public static void main(String[] args) {
		RabbitMqUtils mqUtils = new RabbitMqUtils();
		Connection connection = null;
		try {
			connection = mqUtils.getConnection();
			final Channel chan = connection.createChannel();
			// 回调方式应答,就是服务器发送消息,然后直接在函数内应答
			chan.queueDeclare(queueName, true, false, false, null);

			chan.basicConsume("hello", false, "a-consumer-tag", new DefaultConsumer(chan) {

				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
						byte[] body) throws IOException {
					System.out.println("reject1===>消费者开始处理消息===>" + new String(body, "utf-8")); // 获取传递标记
					
					long deliveryTag = envelope.getDeliveryTag(); // 应用程序发送回确认之后(使用basic.ack方法),队列已经消费成功
					// chan.basicAck(deliveryTag, false);
					try {
						throw new Exception("手动抛出异常");
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						chan.basicReject(deliveryTag, true);// requeue是否重入队列
						System.out.println("出现异常===>消息处理失败!");
						System.out.println("reject1===>开始拒绝消息,并指定该消息返回队列中");
					}
				}
			});

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
/**
 * @description 测试消息拒绝(使用basic.reject拒绝消息)
 * @author hy
 * @date 2020-05-14
 */
public class MessageReject2Test {
	private static final String queueName = "hello";

	public static void main(String[] args) {
		RabbitMqUtils mqUtils = new RabbitMqUtils();
		Connection connection = null;
		try {
			connection = mqUtils.getConnection();
			final Channel chan = connection.createChannel();
			// 回调方式应答,就是服务器发送消息,然后直接在函数内应答
			chan.queueDeclare(queueName, true, false, false, null);

			chan.basicConsume("hello", false, "a-consumer-tag", new DefaultConsumer(chan) {

				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
						byte[] body) throws IOException {
					System.out.println("reject2===>消费者开始处理消息===>" + new String(body, "utf-8")); // 获取传递标记
					long deliveryTag = envelope.getDeliveryTag(); // 应用程序发送回确认之后(使用basic.ack方法),队列已经消费成功
					chan.basicAck(deliveryTag, false);
					System.out.println("reject2===>消息执行成功===>通知服务器消费成功");
				}
			});

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

2.创建消息生产者直接模拟10条消息

public class MessageSenderApplication {
	private final static String QUEUE_NAME = "hello";

	@Test
	public void testSender() {
		RabbitMqUtils mqUtils = new RabbitMqUtils();
		for (int i = 0; i < 10; i++) {
			mqUtils.send("test", "", true, null, "你好,世界!"+(i));
		}
		
	}

}

3.观察消息的拒绝情况
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里发现如果一个消费者拒绝了该消息并添加了requeue为true,那么这个消息就会发送给其他消费者

4.测试拒绝多条消息


/**
 * @description 测试消息拒绝(使用basic.reject拒绝消息)
 * @author hy
 * @date 2020-05-17
 */
public class MessageReject1Test2 {
	public static void main(String[] args) {
		RabbitMqUtils mqUtils = new RabbitMqUtils();
		Connection connection = null;
		try {
			// 一次性获取三条并且使用一次reject查看结果
			connection = mqUtils.getConnection();
			final Channel chan = connection.createChannel();
			GetResponse basicGet = chan.basicGet("hello", false);
			long deliveryTag = basicGet.getEnvelope().getDeliveryTag();
			//chan.basicReject(deliveryTag, true);
			System.out.println("deliveryTag1==>"+deliveryTag);
			
			basicGet = chan.basicGet("hello", false);
			deliveryTag = basicGet.getEnvelope().getDeliveryTag();
			System.out.println("deliveryTag2==>"+deliveryTag);
			
			basicGet = chan.basicGet("hello", false);
			deliveryTag = basicGet.getEnvelope().getDeliveryTag();
			System.out.println("deliveryTag3==>"+deliveryTag);
			
			// 一次性拒绝三条数据
			chan.basicReject(deliveryTag, true);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

然后向启动消息生产者发送10条消息,再次启动MessageReject1Test2拒绝多条消息发现结果如下:
在这里插入图片描述

这里发现了2条未检查的数据,但是上面我reject了3条,结果应该是未检查3条,从这里可以发现basicReject方法实际上不能批量拒绝,只能拒绝一条数据,所以此时未检查数据就是三条(reject一条结果是Ready)

5.分析上面的代码

1.通过分析发现其实消息拒绝时一条一条的拒绝

2.主要通过设定autoAck方式改为显示确认

3.主要调用chan.basicReject(deliveryTag, true);// requeue是否重入队列也可以false直接丢弃

4.这个使用方式和前面的basicAck基本上差不多,必须要有deliveryTag

5.总结

1.拒绝消息,其实就是消息调用中出现问题,无法完成消费任务的情况,这个时候可以设定参数来确认消息是否重新回到队列中,被其他人消费

2.注意拒绝消息的缺点:单个拒绝、必须要有一个能消费的消费者(否则容易出现死循环)

3.需要测试reject能否拒绝多条要通过besicGet方法,直接拉取三条(不能使用basicConsum(这个是被动接收))

以上纯属个人见解,如有问题请联系本人!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值