RabbitMQ: return机制

1. Return机制

Confirm只能保证消息到达exchange,无法保证消息可以被exchange分发到指定queue。

而且exchange是不能持久化消息的,queue是可以持久化消息。

采用Return机制来监听消息是否从exchange送到了指定的queue中

2.Java的实现方式

1.导入依赖

XML 复制代码
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.6.0</version>
        </dependency>

2.生产者的实现方式

采用Return机制来监听消息是否从exchange送到了指定的queue中

java 复制代码
package com.qf.mq2302.hello;

import com.qf.mq2302.utils.MQUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ReturnListener;

import java.io.IOException;

public class SendRetrun {
    public static final String QUEUE_NAME="hello-queue";

    public static void main(String[] args) throws Exception {

        //1.获取连接对象
        Connection conn = MQUtils.getConnection();

        //2. 创建一个channel对象,对于MQ的大部分操作,都定义在了channel对象上
        Channel channel = conn.createChannel();
        
        //3.声明了一个队列
        /**
         * queue -- the name of the queue
         * durable -- true代表创建的队列是持久化的(当mq重启后,该队列依然存在)
         * exclusive -- 该队列是不是排他的 (该对立是否只能由当前创建该队列的连接使用)
         * autoDelete -- 该队列是否可以被mq服务器自动删除
         * arguments -- 队列的其他参数,可以为null
         */
     channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //开启 return 机制
        //编写回调方法
        channel.addReturnListener(new ReturnListener() {
            //如果消息没有成功发送到队列,这个方法会被调用
            @Override
            public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("====================ReturnListener==================");
                System.out.println("replyCode:"+replyCode);
                System.out.println("replyText:"+replyText);
                System.out.println("exchange:"+exchange);
                System.out.println("routingKey:"+routingKey);
                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body,"utf-8"));
                System.out.println("====================ReturnListener==================");
            }
        });


        String message = "Hello doubleasdasda!";

        //生产者如何发送消息,使用下面的方法即可
        /**
         * exchange -- 交换机的名字 ,如果是空串,说明是把消息发给了默认交换机
         * routingKey -- 路由的key,当发送消息给默认交换机时,routingkey代表队列的名字
         * other properties - 消息的其他属性,可以为null
         * body -- 消息的内容,注意,要是有 字节数组
         */
        //注意:如果要使用生产者的return机制,需要在发送消息时,指定mandatory(强制性)为true
        channel.basicPublish("", "sadnaas", true,null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");



    Thread.sleep(1000);

     //   关闭资源
        channel.close();
        conn.close();
    }
}

这个必须要加上才能让rutern返回机制生效

3.消费者的实现方式

java 复制代码
package com.qf.mq2302.hello;

import com.qf.mq2302.utils.MQUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DeliverCallback;
import com.rabbitmq.client.Delivery;

import java.io.IOException;

public class Recv {
   private  final  static  String QUEUE_NAME="hello-queue";

    public static void main(String[] args) throws Exception {
        //1.获取连接对象
        Connection conn = MQUtils.getConnection();

        //2. 创建一个channel对象,对于MQ的大部分操作,都定义在了channel对象上
        Channel channel = conn.createChannel();

        /**
         * 第一个参数队列名称
         * 第二个参数,耐用性
         * 第三个参数排外性
         * 第四个参数是否自动删除
         * 第五个参数,可以定义什么类型的队列
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //3.该消费者收到消息之后的处理逻辑,写在DeliverCallback对象中
        DeliverCallback deliverCallback =new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                System.out.println(consumerTag);
            //从Delivery对象中可以获取到生产者,发送的消息的字节数组
                byte[] body = message.getBody();
                String msg = new String(body, "utf-8");

                //在这里写消费者的业务逻辑,例如,发送邮件
                System.out.println(msg);

            }
        };

        //4.让当前消费者开始消费(QUEUE_NAME)队列中的消息
        /**
         * queue -- the name of the queue
         * autoAck -- true 代表当前消费者是不是自动确认模式。true代表自动确认。
         * deliverCallback -- 当有消息发送给该消费者时,消费者如何处理消息的逻辑
         * cancelCallback -- 当消费者被取消掉时,如果要执行代码,写到这里
         */
      channel.basicConsume(QUEUE_NAME,true,deliverCallback,consumerTag -> {});





    }





}

3.整合springboot实现

1.导入依赖

XML 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2.yml配置文件

XML 复制代码
spring:
  rabbitmq:
    host: 8.140.244.227
    port: 6786
    username: test
    password: test
    virtual-host: /test
    publisher-returns: true #开启return机制

3.RabbitMQ配置文件

java 复制代码
package com.qf.bootmq2302.config;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {


    @Bean
    public RabbitTemplate rabbitTemplate(CachingConnectionFactory cachingConnectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate();

        //设置连接工厂对象
        rabbitTemplate.setConnectionFactory(cachingConnectionFactory);

       // 开启return机制
        rabbitTemplate.setMandatory(true);

         rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
             @Override
             public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                 System.out.println("message:"+new String(message.getBody()));
                 System.out.println("replyCode:"+replyCode);
                 System.out.println("replyText:"+replyText);
                 System.out.println("exchange:"+exchange);
                 System.out.println("routingKey:"+routingKey);
             }
         });

            return rabbitTemplate;
    }



}

4.生产者的controller

java 复制代码
    @Autowired
    RabbitTemplate rabbitTemplate;
    @GetMapping("/test1")
    public String test1(String msg,String routkey){
        System.out.println(msg);
        String exchangeName = "";//默认交换机
        String routingkey = routkey;//队列名字

        //生产者发送消息
        rabbitTemplate.convertAndSend(exchangeName,routingkey,msg);
        return "ok";
    }

5.消费者写一个队列

java 复制代码
   @RabbitListener(queues = "queueA")
    public void getMsg1(Map<String,Object> data, Channel channel,Message message) throws IOException {


        System.out.println(data);

        //手动ack//若开启手动ack,不给手动ack,就按照 prefetch: 1 #等价于basicQos(1)的量,就这么多,不会多给你了,因为你没有确认。确认一条,就给你一条
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

    }

6.消费者的配置文件

java 复制代码
spring:
  rabbitmq:
    host: 8.140.244.227
    port: 6786
    username: test
    password: test
    virtual-host: /test
    #手动ACK
    listener:
      simple:
        acknowledge-mode: manual  # 手动ack
        prefetch: 1 #等价于basicQos(1)
相关推荐
峰子201216 分钟前
B站评论系统的多级存储架构
开发语言·数据库·分布式·后端·golang·tidb
weisian15116 分钟前
消息队列篇--原理篇--Pulsar和Kafka对比分析
分布式·kafka
无锡布里渊39 分钟前
分布式光纤应变监测是一种高精度、分布式的监测技术
分布式·温度监测·分布式光纤测温·厘米级·火灾预警·线型感温火灾监测·分布式光纤应变
40岁的系统架构师1 小时前
15 分布式锁和分布式session
分布式·系统架构
斯普信专业组1 小时前
云原生时代,如何构建高效分布式监控系统
分布式·云原生·prometheus
贾贾20231 小时前
主站集中式和分布式的配电自动化系统区别在哪里?各适用于什么场所?一文详解
运维·分布式·考研·自动化·生活·能源·制造
青灯文案14 小时前
RabbitMQ 匿名队列详解
分布式·rabbitmq
中东大鹅5 小时前
MongoDB基本操作
数据库·分布式·mongodb·hbase
苏苏大大6 小时前
zookeeper
java·分布式·zookeeper·云原生
小高不明7 小时前
仿 RabbitMQ 的消息队列3(实战项目)
java·开发语言·spring·rabbitmq·mybatis