RabbitMQ基础篇(四)-消息应答

概念

根据上一篇文章的思考中得知,如果任务在执行过程中发生故障导致任务中断,并且这条任务也被队列清除掉了,也就是意味着我们就丢失了一条未被执行完的任务。
消息应答的出现可以保证消息在发送过程中不丢失。其工作原理就是,消费者在接收到了队列分配的任务并且处理完这条任务后,告诉RabbitMQ它已经处理完这条任务,可以从队列中清除掉这条任务了。

自动应答

Rabbit MQ默认的应答机制就是自动应答。消息发送后立即被认为已经传送成功。即当消费者接收到消息后立即通知RabbitMQ,Rabbit MQ此时就可以直接从队列中移除该消息。
缺点:很可能会造成消息没有被完成处理就丢失消息,如:

  1. 接收过多,来不及被处理的消息,导致大量消息积压而耗尽内存,最终被操作系统强行终止(线程/进程终止任务)。

  2. 消息处理过程出现问题(Bug),导致程序抛出异常造成消息未处理完毕,而这条消息因为已经被自动应答默认从队列中移除了。

手动应答

手动应答的出现可以很好的处理自动应答的缺点,它可以通过手动控制应答机制,将当前工作线程处理失败的消息通知Rabbit MQ,转交给其他工作线程处理,减低消息丢失的机率。手动应答还可以进行批量应答,减少了网络拥堵的情况,但可能会造成消息不会被完全处理。
生产者:

/**
*
* @author xgs87762
*/
public class RabbitMQAskProducer {
 private static final String TAG = "RabbitMQAsk";
 public static void main(String[] args) throws IOException, TimeoutException {
     Connection connection = RabbitConnectionUtil.getConnection();
     Channel channel = connection.createChannel();
     Map<String, Object> params = new HashMap<>();
     params.put("author", "代码の诱惑");
     params.put("age", "22");

     for (int i = 1; i <= 50; i++) {
         /**
          * 生成队列
          * queue 队列名称
          * durable 是否持久化
          * exclusive 是否独占队列
          * autoDelete 是否自动删除队列(服务器不再使用时自动删除队列)
          * arguments 队列参数
          */
         channel.queueDeclare(TAG, false, false, false, params);
         String message = "发布消息";
         channel.basicPublish("", TAG, null, message.getBytes(StandardCharsets.UTF_8));
         System.out.println("发送了" + i + "条消息");
     }
 }
}

消费者

/**
* RabbitMQAskCustomer.java
* TODO
* Created on 2023/2/26 18:22
* 1、测试工作线程消费情况
* 2、测试工作线程出现异常,任务自动分配给其他正常工作线程消费
* @author xgs87762
* @version V1.0
*/
public class RabbitMQAskCustomer implements Runnable {
 // 队列名称
 private static final String TAG = "RabbitMQAsk";
 // 计算已执行成功任务数
 private static Integer counter = 0;

 public static void main(String[] args) {
     Runnable askCustomer = new RabbitMQAskCustomer();
     for (int i = 1; i <= 10; i++) {
         new Thread(askCustomer, "线程" + i).start();
     }
 }

 @Override
 public void run() {
     try {
         String threadName = Thread.currentThread().getName();
         Connection connection = RabbitConnectionUtil.getConnection();
         Channel channel = connection.createChannel();
         DeliverCallback deliverCallback = (consumerTag, message) -> {
             // 消息接收监听
             System.out.println(threadName + " ==> 准备开展工作...");
             RabbitMQAskCustomer.thread();

             // 测试:测试线程执行失败,观察工作任务情况
             // 结论:线程8出现问题后,队列自动将工作任务分配给了其他线程消费。
             if ("线程8".equals(threadName)) {
                 throw new IOException("Thread " + threadName + "异常");
             }
             System.out.println(threadName + " ==> 工作执行完毕!");

             /**
              * 消息应答
              * deliverTag 消息的标记 tag,表明消息的唯一标识
              * multiple 是否批量应答(一般不允许应答,防止消息丢失)
              */
             channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
             System.out.println(threadName + " ==> 消息应答提交完毕!");
             counter++;
             System.out.println("已执行 》" + counter + "《 条任务!");
         };
         CancelCallback cancelCallback = (consumerTag) -> {
             System.out.println(threadName + " ==> 消息取消");
         };
         channel.basicConsume(TAG, false, deliverCallback, cancelCallback);
     } catch (IOException | TimeoutException e) {
         e.printStackTrace();
     }
 }

 public static void thread() {
     try {
         Thread.sleep(2000);
     } catch (InterruptedException e) {
         throw new RuntimeException(e);
     }
 }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值