RabbitMQ

RabbitMQ

一、什么是MQ

MQ(message queue),字面意思就是一个队列,FIFO先入先出,只不过队列中存放的内容是message,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用MQ之后,消息发送上游只需要依赖MQ,不用依赖其他服务

二、MQ的优点
  • 流量消峰
  • 应用解耦
  • 异步处理
三、rabbitMQ

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

四、docker安装
  • –name :指定容器名称
  • -p :将mq端口号映射到本地
  • RABBITMQ_DEFAULT_USER 设置用户名
docker run -di --name myrabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 5672:5672 -p 15672:15672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management

启动
在这里插入图片描述

访问
在这里插入图片描述

五、rabbitmq的工作模式

官网:https://www.rabbitmq.com/getstarted.html

  • 5.1、简单模式

img

一个生产者对应一个消费者!!

  • 5.2、工作模式

img

一个生产者对应多个消费者,但是一条消息只能有一个消费者获得消息!!!
轮询分发就是将消息队列中的消息,依次发送给所有消费者。一个消息只能被一个消费者获取。

公平分发:根据消费者能力进行分发,处理快的分配的多

  • 5.3、订阅模式

img

一个消费者将消息首先发送到交换器,交换器绑定到多个队列,然后被监听该队列的消费者所接收并消费。

ps:X表示交换器,在RabbitMQ中,交换器主要有四种类型:direct、fanout、topic、headers,这里的交换器是 fanout。下面我们会详细介绍这几种交换器。
  ==两个消费者获得了同一条消息。==即就是,一个消息从交换机同时发送给了两个队列中,监听这两个队列的消费者消费了这个消息;
如果没有队列绑定交换机,则消息将丢失。因为交换机没有存储能力,消息只能存储在队列中。

  • 5.4、路由模式

img

生产者将消息发送到direct交换器,在绑定队列和交换器的时候有一个路由key,生产者发送的消息会指定一个路由key,那么消息只会发送到相应key相同的队列,接着监听该队列的消费者消费消息。

也就是让消费者有选择性的接收消息
路由模式,是以路由规则为导向,引导消息存入符合规则的队列中。再由队列的消费者进行消费的。

  • 5.5、主题模式

img

上面的路由模式是根据路由key进行完整的匹配(完全相等才发送消息),这里的通配符模式通俗的来讲就是模糊匹配

符号“#”表示匹配一个或多个词,符号“*”表示匹配一个词。
  与路由模式相似,但是,主题模式是一种模糊的匹配方式。

六、四种交换机
  • 1、direct 如果路由键完全匹配的话,消息才会被投放到相应的队列。
  • 2、fanout 当发送一条消息到fanout交换器上时,它会把消息投放到所有附加在此交换器上的队列。
  • 3、topic 设置模糊的绑定方式,“*”操作符将“.”视为分隔符,匹配单个字符;“#”操作符没有分块的概念,它将任意“.”均视为关键字的匹配部分,能够匹配多个字符。
  • 4、header headers 交换器允许匹配 AMQP 消息的 header 而非路由键,除此之外,header 交换器和 direct 交换器完全一致,但是性能却差很多,因此基本上不会用到该交换器
七、简单模式案例
 <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.2.0</version>
        </dependency>
  • 生产者
public class Producer {
    public static void main(String[] args) {
        //所有中间件技术都是基于tcp/ip协议基础上构建新型的协议规范,只不过rabbitmq遵循的是amqp协议

        //1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("139.196.90.55");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        //设置虚拟访问节点
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接
            connection = connectionFactory.newConnection("生产者1号");
            //3.通过连接创建通道channel
            channel = connection.createChannel();
            //4.通过创建交换机声明队列
            /**
             * queueDeclare(String queue,         队列名称
             *              boolean durable,      是否持久化      非持久化也会存盘,但是rabbit服务重启则就没有了
             *              boolean exclusive,    排他性,是否独占
             *              boolean autoDelete,   是否自动删除,随着最后一个消费者吧消息消费后时候把队列自动删除
             *              Map<String, Object> arguments  携带附属参数
             *              )
             */
            String queName = "queue1";
            channel.queueDeclare(queName, false, false, false, null);
            //5.准备消息
            String message = "hello";
            //6.发送消息给队列queue
            //@params1:交换机 @@params2:队列,路由key @params3:消息的状态控制 @params4:消息
            channel.basicPublish("", queName, null, message.getBytes());

            System.out.println("消息发送成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //7.关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 消费者
public class Consumer {
    public static void main(String[] args) {
        //所有中间件技术都是基于tcp/ip协议基础上构建新型的协议规范,只不过rabbitmq遵循的是amqp协议

        //1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("139.196.90.55");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");

        Connection connection =null;
        Channel channel = null;
        try {
            //2.创建连接
            connection = connectionFactory.newConnection();
            //3.通过连接创建通道channel
            channel = connection.createChannel();

            //4.消费者消费消息
            /**
             *basicConsume(String queue,  消费哪个队列
             *  boolean autoAck,          消费成功之后是否要自动应答 true 代表自动应答,false代表收到应答
             *  DeliverCallback deliverCallback, 收到消息后用来处理消息的回调对象
             *  CancelCallback cancelCallback    消费者取消时的回调对象
             *  )
             */
            channel.basicConsume("queue1", true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    System.out.println("消费消息是" + new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    System.out.println("取消消费的");
                }
            });

            System.out.println("开始接收消息");
            System.in.read();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //6.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
八、rabbitmq的核心组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tpUryA7-1652768042828)(…/AppData/Roaming/Typora/typora-user-images/1652081313901.png)]

核心概念:

Seerver(Broker):接受客户端的连接,实现AMQP实体服务

Connection:连接,应用程序与Broker的网络连接TCP/IP 三次握手和四次挥手

Channel:网络信道,几乎所有的操作都在channel中进行,Channel是进行消息读写的通道,客户端可以建立对各Channel,每个Channel代表一个会话任务

Message:消息,服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,比如消息的优先级,延迟等高级特性,Body则就是消息内容

Virtual Host:虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若干个Exchange和Queue,同一个虚拟主机里面不能有相同名字的Exchange

Exchange:交换机,接受消息,根据路由键发送消息到绑定的队列 不具备消息存储能力

Bindings :Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key

Routing key:路由规则,虚拟机可以用它来确定如何路由一个特定消息

Queue:队列,消息队列,保存消息并将它们转发给消费者

九、fanout(订阅)模式案例
  • 消费者,用多线程模拟两个消费则
public class Consumer{
    public static void main(String[] args) {
        new Thread(runnable,"线程一").start();
        new Thread(runnable,"线程二").start();
    }

    private static Runnable runnable = new Runnable() {
        private static final String EXCHANGE_NAME ="aaa";
        @SneakyThrows
        @Override
        public void run() {
            //创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("139.196.90.55");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            //创建连接
            Connection connection = connectionFactory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //声明交换机 durable设为true 表示交换机持久化
            channel.exchangeDeclare(EXCHANGE_NAME, "fanout",true);

            //声明临时队列  当消费者断开与队列的连接时,队列就自动删除 队列名称随机
            String queueName = channel.queueDeclare().getQueue();

            //绑定交换机与队列
            channel.queueBind(queueName, EXCHANGE_NAME, "");

            //接收消息
            System.out.println(Thread.currentThread().getName()+"开始接收消息");
            //接收消息
            DeliverCallback deliverCallback =(consumerTag,message) ->{
                System.out.println(Thread.currentThread().getName()+"接收到的消息是"+new String(message.getBody(),"UTF-8"));
            };
            //消费者取消消息
            CancelCallback cancelCallback =(consumerTag)->{
                System.out.println("消费者取消消费"+"::"+consumerTag);
            };

            channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
            System.in.read();
            channel.close();
            connection.close();
        }
    };
}
  • 生产者
public class Producer {
    private static final String EXCHANGE_NAME ="aaa";
    public static void main(String[] args) throws Exception {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("139.196.90.55");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //声明交换机 durable设为true 表示交换机持久化
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout",true);
        //发送消息
        String message = "急急急急";
        channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes("UTF-8"));
        
        channel.close();
        connection.close();
    }
}
十、direct(路由)模式

路由模式只是在订阅模式基础上,交换机改为“direct”,绑定队列指定路由key,模仿订阅案例即可

十一、topic(主题)模式

交换机改为 topic 路由规则改为模糊匹配

十二、工作模式
轮询分发工作模式
  • 生产者
public class Producer {
    public static void main(String[] args) {
        //1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("139.196.90.55");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        //设置虚拟访问节点
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接
            connection = connectionFactory.newConnection("生产者1号");
            //3.通过连接创建通道channel
            channel = connection.createChannel();
            //4.通过创建交换机声明队列
            /**
             * queueDeclare(String queue,         队列名称
             *              boolean durable,      是否持久化      非持久化也会存盘,但是rabbit服务重启则就没有了
             *              boolean exclusive,    排他性,是否独占
             *              boolean autoDelete,   是否自动删除,随着最后一个消费者吧消息消费后时候把队列自动删除
             *              Map<String, Object> arguments  携带附属参数
             *              )
             */
            String queName = "queue1";
            channel.queueDeclare(queName, false, false, false, null);
            
            //5.发送消息给队列queue
            for (int i = 0; i < 20; i++) {
                String message = "你好" + i;
                channel.basicPublish("",queName,null,message.getBytes("UTF-8"));
            }
            System.out.println("消息发送成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //7.关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } }}
  • 消费者
public class Worker1 {
    public static void main(String[] args) {
        //1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("139.196.90.55");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection =null;
        Channel channel = null;
        try {
            //2.创建连接
            connection = connectionFactory.newConnection();
            //3.通过连接创建通道channel
            channel = connection.createChannel();
            String queueName = "queue1";
            //4.声明队列
            /**
             * queueDeclare(String queue,         队列名称
             *              boolean durable,      是否持久化      非持久化也会存盘,但是rabbit服务重启则就没有了
             *              boolean exclusive,    排他性,是否独占
             *              boolean autoDelete,   是否自动删除,随着最后一个消费者吧消息消费后时候把队列自动删除
             *              Map<String, Object> arguments  携带附属参数
             *              )
             */
            channel.queueDeclare(queueName, false, false, false, null);
            //5.消费者消费消息
            /**
             *basicConsume(String queue,  消费哪个队列
             *  boolean autoAck,          消费成功之后是否要自动应答 true 代表自动应答,false代表收到应答
             *  DeliverCallback deliverCallback, 收到消息后用来处理消息的回调对象
             *  CancelCallback cancelCallback    消费者取消时的回调对象
             *  )
             */
            channel.basicConsume(queueName, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    System.out.println("Worker1消费消息是" + new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    System.out.println("消费者取消消费");
                }
            });

            System.out.println("Worker1开始接收消息");
            System.in.read();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //7.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

// worker2
public class Worker2 {
    public static void main(String[] args) {
        //1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("139.196.90.55");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection =null;
        Channel channel = null;
        try {
            //2.创建连接
            connection = connectionFactory.newConnection();
            //3.通过连接创建通道channel
            channel = connection.createChannel();
            String queueName = "queue1";
            //4.声明队列

            channel.queueDeclare(queueName, false, false, false, null);

            channel.basicConsume(queueName, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    System.out.println("Worker2消费消息是" + new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    System.out.println("消费者取消消费");
                }
            });

            System.out.println("Worker2开始接收消息");
            System.in.read();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //7.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
公平分发工作模式
  • 区别
    • 消费者开启手动应答
    • 开启QOS qos数根据服务器性能设置, qos限制一次消费的消息数量
 Channel finalChannel = channel;
            channel.basicQos(1);  //设置qos
            channel.basicConsume(queueName, false, new DeliverCallback() { //开启手动应答
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    System.out.println("Worker1消费消息是" + new String(message.getBody(), "UTF-8"));
                    //开启手动应答  参数1:消息标记 参数2:fasle单个消息应答,true:批量应答
                    finalChannel.basicAck(message.getEnvelope().getDeliveryTag(),false);
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    System.out.println("消费者取消消费");
                }
            });

十三、springboot整合rabbitmq --fanout案例
  • 依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

  • ymi配置
server:
  port: 8080

# rabbitmq 配置
spring:
  rabbitmq:
    host: 139.196.90.55
    username: admin
    password: admin
    virtual-host: /

  • 配置交换机与队列绑定
@Configuration
public class RabbitMqConfig {
    public static final String EXCHANGE_NAME = "fanout_exchange";
    public static final String WEIXIN_QUEUE = "weixin_queue";
    public static final String EMAIL_QUEUE = "email_queue";
    //声明交换机 fanout类型
    @Bean
    public FanoutExchange fanoutExchange(){
        /**
         * FanoutExchange(String name,  交换机名称
         *                boolean durable,  是否持久化
         *                boolean autoDelete 是否自动删除
         *                )
         */
        return new FanoutExchange(EXCHANGE_NAME,false,false);
    }

    //声明队列
    @Bean
    public Queue weiXinQueue(){
        return new Queue(WEIXIN_QUEUE, false);
    }

    @Bean
    public Queue emailQueue(){
        return new Queue(EMAIL_QUEUE, false);
    }

    //完成交换机与队列的绑定
    @Bean
    public Binding weiXinBinding(){
        return BindingBuilder.bind(weiXinQueue()).to(fanoutExchange());
    }

    @Bean
    public Binding emailBinding(){
        return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
    }
}

  • 生产者
@Component
public class Producer {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void sendMessage(){
        String routingKey = "";
        for (int i = 0; i < 10; i++) {
            String message = "真的好" + i;
            rabbitTemplate.convertAndSend(RabbitMqConfig.EXCHANGE_NAME,routingKey,message);
        }
    }
}

  • 消费者
//微信
@Component
@RabbitListener(queues = {RabbitMqConfig.WEIXIN_QUEUE})  //指定监听的队列
public class WeiXin {

    @RabbitHandler
    public void receiveMessage(String message){
        System.out.println(RabbitMqConfig.WEIXIN_QUEUE+"---接收到的信息是---->"+message);
    }
}

//email
@Component
@RabbitListener(queues = {RabbitMqConfig.EMAIL_QUEUE})  //指定监听的队列
public class Email {

    @RabbitHandler
    public void receiveMessage(String message){
        System.out.println(RabbitMqConfig.EMAIL_QUEUE+"---接收到的信息是---->"+message);
    }
}

  • 测试
 @Test
void test1(){
   producer.sendMessage();
}

十四、direct模式–springboot
  • 只是在绑定交换机和队列时加个路由key
  @Bean
    public DirectExchange directExchange(){
        return new DirectExchange(EXCHANGE_NAME_DIRECT, false, false);
    }

    @Bean
    public Binding emailBinding1(){
        return BindingBuilder.bind(emailQueue()).to(directExchange()).with("routingKeyEmail");
    }

十五、使用注解完成 topic主题模式
  • 生产者
@Component
public class Producer {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void sendTopicMessage(){
        String routingKey = "com.email";
        for (int i = 0; i < 10; i++) {
            String message = "真的好" + i;
            rabbitTemplate.convertAndSend("topic_exchange",routingKey,message);
        }
    }}

  • 消费者+绑定队列与交互机+指定类型
//email
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "email_topic_queue",durable = "false",autoDelete = "false"),
        exchange = @Exchange(value = "topic_exchange",type = ExchangeTypes.TOPIC),
        key = "#.email.#"
))
public class Email {

    @RabbitHandler
    public void receiveMessage(String message){
        System.out.println("email_topic_queue---接收到的信息是---->"+message);
    }
}

//weixin
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "weixin_topic_queue",durable = "false",autoDelete = "false"),
        exchange = @Exchange(value = "topic_exchange",type = ExchangeTypes.TOPIC),
        key = "*.weixin.#"
))
public class WeiXin {

    @RabbitHandler
    public void receiveMessage(String message){
        System.out.println("weixin_topic_queue---接收到的信息是---->"+message);
    }
}

十六、过期时间TTL

过期时间ttl表示可以对消息设置预期的时间,在时间内可以被消费者消费,过了时间自动删除

两种ttl设置方式:

  • 第一种:通过队列属性设置,整个队列中的消息都有相同过期时间
  • 第二种:单独对每条消息设置ttl。

如果上述两种方法同时使用,则消息过期时间以较小的为准

给队列设置过期时间,消息过期后可进入死信队列

16.1、给队列配置ttl (第一种)
  • 配置类中配置队列过期时间
@Configuration
public class TtlRabbitMqConfig {
    @Bean
    public DirectExchange ttlDirectExchange() {
        return new DirectExchange("ttl_direct_exchange", false, false);
    }
    @Bean
    public Queue ttlQueue(){
        HashMap<String,Object> map = new HashMap<>();
        //设置过期时间  5秒
        map.put("x-message-ttl", 5000);
        return new Queue("ttl_queue", false, false, false, map);
    }
    @Bean
    public Binding ttlBinding(){
       return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
    }
}

  • 发送消息
@Component
public class Producer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendTtlMessage(){
        String routingKey = "ttl";
        String message = "真的好";
        rabbitTemplate.convertAndSend("ttl_direct_exchange",routingKey,message);
    }}

//测试
    @Test
    void test1(){
        producer.sendTtlMessage();
    }

16.2、给消息配置ttl
   public void sendTtlMessage(){
        String routingKey = "ttl";
        String message = "真的好";
        rabbitTemplate.convertAndSend("ttl_direct_exchange",routingKey,message,new MessagePostProcessor(){
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //给消息设置过期时间
                message.getMessageProperties().setExpiration("3000");
                return message;
            }
        });
    }

十七、死信队列

死信队列-: 当消息在队列中变成死信后,可根据绑定让消息进入死信队列

三种消息进入死信队列

  • 消息被拒绝
  • 消息过期
  • 队列达到最大长度

模拟消息过期

@Configuration
public class TtlRabbitMqConfig {

    @Bean
    public DirectExchange ttlDirectExchange() {
        return new DirectExchange("ttl_direct_exchange", false, false);
    }
    @Bean
    public Queue ttlQueue(){
        HashMap<String,Object> map = new HashMap<>();
        //设置过期时间  5秒
        map.put("x-message-ttl", 5000);
        //设置绑定的死信交换机  过期队列绑定死信交换机,过期消息进入死信队列
        map.put("x-dead-letter-exchange", "dead_direct_exchange");
        map.put("x-dead-letter-routing-key", "dead");
        return new Queue("ttl_queue", false, false, false, map);
    }
    @Bean
    public Binding ttlBinding(){
       return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
    }

    //声明死信队列交换机
    @Bean
    public DirectExchange deadDirectExchange(){
        return new DirectExchange("dead_direct_exchange", true,false);
    }

    //声明死信队列
    @Bean
    public Queue deadQueue(){
        return new Queue("dead_queue", true);
    }

    //绑定死信队列与交换机
    @Bean
    public Binding deadBinding(){
        return BindingBuilder.bind(deadQueue()).to(deadDirectExchange()).with("dead");
    }
}

十八、发布消息确认 (交换机)

确认消息发送到交换机

  • 配置类
# rabbitmq 配置
spring:
  rabbitmq:
    host: 139.196.90.55
    username: admin
    password: admin
    virtual-host: /
    publisher-confirm-type: correlated   #开启发布确认

  • 回调接口
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        //注入
        rabbitTemplate.setConfirmCallback(this);
    }
    /**
     * 消息回调
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String s) {
        String id =correlationData != null ? correlationData.getId() : "";
        if (!ack){
            System.out.println("交换机未收到消息::id=="+id + "::cause::"+s);
        }else {
            System.out.println("交换机收到消息::id=="+id + "::cause::"+s);
        }
    }
}

  • 发送消息
    public void sendTtlMessage(){
        String routingKey = "ttl";
        String message = "真的好";
        CorrelationData correlationData = new CorrelationData("1");  //设置id
        rabbitTemplate.convertAndSend("ttl_direct_exchange",routingKey,message,correlationData);
    }

十九、消息退回(队列)

交换机未发送到队列

需要开启配置:

spring:
  rabbitmq:
    publisher-confirm-type: correlated   #开启发布确认
    publisher-returns: true  #交换机发给队列失败 退回

@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        //注入 交换机回退接口
        rabbitTemplate.setConfirmCallback(this);
        //注入 队列回退接口
        rabbitTemplate.setReturnsCallback(this);
    }
    /**
     * 消息回调 发送到交换机
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String s) {
        String id =correlationData != null ? correlationData.getId() : "";
        if (!ack){
            System.out.println("交换机未收到消息::id=="+id + "::cause::"+s);
        }else {
            System.out.println("交换机收到消息::id=="+id + "::cause::"+s);
        }
    }

    // 交换机发送给队列不成功回调
    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
        System.out.println("消息未发送到队列::"+returnedMessage.getMessage()+"exchange::"+returnedMessage.getExchange()+"::routingkey::"+returnedMessage.getRoutingKey());
    }
}

二十、消费可靠性

消费出现异常时,如何保证消费可靠性

解决消息重试的几种方案:

1.控制重发的次数
2.try+catch+手动ack
3.try+catch +手动ack+死信队列+人工干预

可参考:

https://blog.csdn.net/qq_41432730/article/details/120723446

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值