RabbitMQ进阶

MQ进阶:
    
    1.springboot整合rabbitMQ:
        为了方便,这里只是模拟使用场景,所以不创建多个系统了,自己给自己发消息;
        1.创建工程it-springboot-rabbitmq,添加依赖:
            <?xml version="1.0" encoding="UTF-8"?>
            <project xmlns="http://maven.apache.org/POM/4.0.0"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                <modelVersion>4.0.0</modelVersion>

                <groupId>com.it</groupId>
                <artifactId>it-springboot-rabbitmq</artifactId>
                <version>1.0-SNAPSHOT</version>

                <!--父工程-->
                <parent>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-parent</artifactId>
                    <version>2.1.4.RELEASE</version>
                </parent>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-amqp</artifactId>
                    </dependency>
                    <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                    </dependency>
                </dependencies>

                <repositories>
                    <repository>
                        <id>alimaven</id>
                        <name>aliyun maven</name>
                        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
                        <releases>
                            <enabled>true</enabled>
                        </releases>
                        <snapshots>
                            <enabled>false</enabled>
                        </snapshots>
                    </repository>
                </repositories>


            </project>
        2.配置文件application.yml(实际使用过程中,生产者端和消费者端都要配置连接信息):
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                virtual-host: /lyle
                username: lyle
                password: lyle
              application:
                name: springboot-rabbitmq
                
        3.创建启动类,并在启动类中利用注解@Bean注册队列,注册交换机,将队列绑定到交换机
            package it.com;

            import org.springframework.amqp.core.Binding;
            import org.springframework.amqp.core.BindingBuilder;
            import org.springframework.amqp.core.Queue;
            import org.springframework.amqp.core.TopicExchange;
            import org.springframework.boot.SpringApplication;
            import org.springframework.boot.autoconfigure.SpringBootApplication;
            import org.springframework.context.annotation.Bean;

            /**
             * ToDo
             *
             * @author Lyle
             * @date 2020/4/5
             */
            @SpringBootApplication
            public class RabbitMQApplication {
                public static void main(String[] args) {
                    SpringApplication.run(RabbitMQApplication.class,args);
                }
                /**
                 * 发送消息选择通配符模式
                 */

                //创建队列,指定队列名称
                @Bean
                public Queue createQueue(){
                    return new Queue("topic_springboot_queue");
                }

                //创建交换机,指定交换机名称
                @Bean
                public TopicExchange createExchange(){
                    return new TopicExchange("topic_springboot_exchange");
                }

                //将队列绑定到交换机
                @Bean
                public Binding createBinding(){
                    return BindingBuilder.bind(createQueue()).to(createExchange()).with("item.*");
                }
            }
        4.创建类,使用RabbitTemplate发送消息:
            package it.com;

            import org.junit.Test;
            import org.junit.runner.RunWith;
            import org.springframework.amqp.rabbit.core.RabbitTemplate;
            import org.springframework.beans.factory.annotation.Autowired;
            import org.springframework.boot.test.context.SpringBootTest;
            import org.springframework.test.context.junit4.SpringRunner;

            /**
             * ToDo
             *
             * @author Lyle
             * @date 2020/4/5
             */
            @RunWith(SpringRunner.class)
            @SpringBootTest
            public class RabbitMQTest {
                //用于发送MQ消息
                @Autowired
                private RabbitTemplate rabbitTemplate;
                @Test
                public void testCreateMessage(){
                    rabbitTemplate.convertAndSend("topic_springboot_exchange", "item.insert", "商品新增,routing key 为item.insert");
                    rabbitTemplate.convertAndSend("topic_springboot_exchange", "item.update", "商品修改,routing key 为item.update");
                    rabbitTemplate.convertAndSend("topic_springboot_exchange", "item.delete", "商品删除,routing key 为item.delete");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        5.创建类并交给spring管理,监听并处理消息
            注解:@RabbitListener(queues = "topic_springboot_queue"),用在类上,指定要监听的队列
            注解:@RabbitHandler,用在方法上,该注解可以根据不同的参数类型,调用响应的方法处理消息.
            package it.com.listener;

            import org.springframework.amqp.rabbit.annotation.RabbitHandler;
            import org.springframework.amqp.rabbit.annotation.RabbitListener;
            import org.springframework.stereotype.Component;

            /**
             * ToDo
             *
             * @author Lyle
             * @date 2020/4/5
             */
            @Component
            @RabbitListener(queues = "topic_springboot_queue")
            public class MyMessageListener {
                /**
                 * 接收并处理消息
                 * @param msg
                 */
                @RabbitHandler
                public void receiveMessage(String msg){

                    System.out.println("message:"+msg);
                }


            }
            
2.rabbitMQ高级特性:
    生产者可靠性消息投递:
        confirm模式:生产者发送消息到交换机的处理模式;
                    只确认消息是否有到达交换机,至于消息是否从交换机到达队列,不管;
        
        return:生产者发送的消息已经到了交换机,确认消息是否从交换机到达对列;
    confirm机制演示:
        1.创建工程,导入依赖:
            <?xml version="1.0" encoding="UTF-8"?>
            <project xmlns="http://maven.apache.org/POM/4.0.0"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                <modelVersion>4.0.0</modelVersion>

                <groupId>com.it</groupId>
                <artifactId>it-springboot-rabbitMQ-day02</artifactId>
                <version>1.0-SNAPSHOT</version>

                <!--父工程-->
                <parent>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-parent</artifactId>
                    <version>2.1.4.RELEASE</version>
                </parent>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-web</artifactId>
                    </dependency>

                    <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-amqp</artifactId>
                    </dependency>
                    <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>

                    </dependency>
                </dependencies>

            </project>
        2.创建配置文件,配置连接信息,并配置开启confirm机制:
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                virtual-host: /lyle
                username: lyle
                password: lyle
                #开启confirm机制
                publisher-confirms: true

              application:
                name: springboot-rabbitmq
        3.创建启动类,并在启动类中注册队列,注册交换机,将队列绑定到交换机
            package com.it;

            import org.springframework.amqp.core.Binding;
            import org.springframework.amqp.core.BindingBuilder;
            import org.springframework.amqp.core.DirectExchange;
            import org.springframework.amqp.core.Queue;
            import org.springframework.boot.SpringApplication;
            import org.springframework.boot.autoconfigure.SpringBootApplication;
            import org.springframework.context.annotation.Bean;

            /**
             * ToDo
             *
             * @author Lyle
             * @date 2020/4/5
             */
            @SpringBootApplication
            public class RabbitmqApplication {
                public static void main(String[] args) {
                    SpringApplication.run(RabbitmqApplication.class,args);
                }

                //创建队列,指定交换机名称
                @Bean
                public Queue createQueue(){
                    return new Queue("queue_demo01");
                }
                //创建交换机,指定交换机名称
                @Bean
                public DirectExchange createExchange(){
                    return new DirectExchange("exchange_direct_demo01");
                }
                //创建绑定,将队列绑定到交换机
                @Bean
                public Binding createBinding(){
                    return BindingBuilder.bind(createQueue()).to(createExchange()).with("item.insert");
                }
            }
        4.创建类交给spring管理,该类实现RabbitTemplate.ConfirmCallback,重写接口方法用于处理回调逻辑
            package com.it.confirm;

            import org.springframework.amqp.rabbit.connection.CorrelationData;
            import org.springframework.amqp.rabbit.core.RabbitTemplate;
            import org.springframework.stereotype.Component;

            /**
             * ConfirmCallback,该接口用于confirm机制中的回调
             *
             * @author Lyle
             * @date 2020/4/6
             */
            @Component
            public class MyConfirmCallback implements RabbitTemplate.ConfirmCallback {
                /**
                 *
                 * @param correlationData:数据信息
                 * @param b:标记交换机是否已经收到消息,true则表示收到消息
                 * @param cause:错误日志,发送成功则为null
                 */
                @Override
                public void confirm(CorrelationData correlationData, boolean b, String cause) {
                    //处理回调逻辑
                    if (b){
                        //值为true,消息发送成功
                        System.out.println("消息发送成功...");
                    }else {
                        //消息发送失败,并打印错误信息
                        System.out.println("消息发送失败.."+cause);
                        //消息失败后的处理逻辑

                    }
                }
            }
        5.创建Controller类,注入RabbitTemplate,在浏览器中发送请求,模拟发送消息到交换机;
            注入RabbitTemplate.ConfirmCallback接口的实现类,并在发送消息前设置回调函数;
            发送消息会自动调用回调函数进行处理;
            package com.it.controller;

            import com.it.confirm.MyConfirmCallback;
            import org.springframework.amqp.rabbit.core.RabbitTemplate;
            import org.springframework.beans.factory.annotation.Autowired;
            import org.springframework.web.bind.annotation.RequestMapping;
            import org.springframework.web.bind.annotation.RestController;

            /**
             * ToDo
             *
             * @author Lyle
             * @date 2020/4/5
             */
            @RestController
            @RequestMapping("/test")
            public class TestController {

                @Autowired
                private RabbitTemplate rabbitTemplate;

                @Autowired
                private MyConfirmCallback myConfirmCallback;

                @RequestMapping("/send1")
                public String send1(){
                    System.out.println("更新了数据....");
                    //在发送消息前设置回调函数:myConfirmCallback
                    rabbitTemplate.setConfirmCallback(myConfirmCallback);

                    rabbitTemplate.convertAndSend("exchange_direct_demo01","item.insert","hello insert...");
                    return "ok";
                }
            }
            
    return 机制的演示:
        1.在配置文件中配置开启return机制:
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                virtual-host: /lyle
                username: lyle
                password: lyle
                #开启confirm机制
                publisher-confirms: true
                # 开启return机制
                publisher-returns: true

              application:
                name: springboot-rabbitmq
        2.创建类交给spring管理,该类实现接口RabbitTemplate.ReturnCallback,重写接口方法用于处理回调逻辑:
            package com.it.returncall;

            import org.springframework.amqp.core.Message;
            import org.springframework.amqp.rabbit.core.RabbitTemplate;
            import org.springframework.stereotype.Component;

            /**
             * RabbitTemplate.ReturnCallback:该接口用于处理交换机路由消息到队列的异常处理
             *
             * @author Lyle
             * @date 2020/4/6
             */
            @Component
            public class MyReturnCallback implements RabbitTemplate.ReturnCallback {
                /**
                 *
                 * @param message:消息本身
                 * @param replyCode:错误码
                 * @param replyText:错误信息
                 * @param exchange:发生错误的交换机
                 * @param routingkey:发生错误时的routingkey
                 */
                @Override
                public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingkey) {
                    //出现错误后,处理相关回滚业务
                    System.out.println("消息本身:"+new String(message.getBody()));
                    System.out.println("错误码:"+replyCode);
                    System.out.println("错误信息:"+replyText);
                    System.out.println("交换机:"+exchange);
                    System.out.println("routingkey:"+routingkey);
                    //回滚数据
                    System.out.println("回滚了数据...");
                }
            }
        3.模拟发送消息,在发送消息前设置回调函数:
            @RequestMapping("/send2")
            public String send2(){
                System.out.println("更新了数据....");
                //在发送消息前设置回调函数:myReturnCallback
                //写错routingkey,交换机路由消息到对列会出错,会执行回调函数
                rabbitTemplate.setReturnCallback(myReturnCallback);

                rabbitTemplate.convertAndSend("exchange_direct_demo01","item.insert1234","hello insert...");
                return "ok";
            }
            
    消费者ACK确认机制:
        自动确认:acknowledge="none",默认的机制
        手动确认:acknowledge="manual"
        根据异常情况来确认:acknowledge="auto"
    
        手动确认机制:
            手动签收策略:
                确认收到;
                拒绝签收,并丢弃/或者重回队列; .....可以批量
                拒绝签收,并丢弃/或者重回队列; .....不能批量处理
                
            1.在配置文件中配置ACK确认机制为手动模式:
                spring:
                  rabbitmq:
                    host: localhost
                    port: 5672
                    virtual-host: /lyle
                    username: lyle
                    password: lyle
                    #开启confirm机制
                    publisher-confirms: true
                    # 开启return机制
                    publisher-returns: true
                    # 配置ACK确认机制为手动模式:
                    listener:
                      direct:
                        acknowledge-mode: manual

                  application:
                    name: springboot-rabbitmq
            2.编写类和方法进行手动签收确认/或者拒绝签收
                package com.it.listener;

                import com.rabbitmq.client.Channel;
                import org.springframework.amqp.core.Message;
                import org.springframework.amqp.rabbit.annotation.RabbitHandler;
                import org.springframework.amqp.rabbit.annotation.RabbitListener;
                import org.springframework.stereotype.Component;

                import java.io.IOException;

                /**
                 * ToDo
                 *
                 * @author Lyle
                 * @date 2020/4/6
                 */
                @Component
                @RabbitListener(queues = "queue_demo01")
                public class MyMessageListener {

                    @RabbitHandler
                    public void receiveMessage(Message message, String msg, Channel channel){
                        //接收到了消息
                        System.out.println("message:"+msg);
                        try {
                            //处理业务
                            System.out.println("哈哈哈收到了1000块");
                            //int i=1/0;
                            //业务处理完成,手动签收
                            /**
                             *参数2:是否批量处理
                             */
                            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);//确认签收,不批量处理
                        } catch (Exception e) {

                            e.printStackTrace();
                            try {
                                /**
                                 * //出现异常,拒绝签收 ,直接丢弃
                                 * 参数2: 是否批量
                                 * 参数3:是否重回队列
                                 * 若是出错后,选择重回队列,该消息会在队列的最前面,重新被消费,这样会陷入死循环
                                 */
                                //channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
                                channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
                            } catch (Exception e1) {
                                e1.printStackTrace();
                            }

                        }


                    }
                }
                
    消费端限流设置:
        在配置文件中进行配置prefetch的值,默认值为250:
            listener:
              simple:
                # 配置ACK确认机制为手动模式:
                acknowledge-mode: manual
                # 设置每一个消费端可以处理未确认的消息的最大数量
                prefetch: 2
                
    TTL:全称 Time To Live(指消息的存活时间/过期时间)
        当消息到达存活时间后,还没有被消费,会被自动清除。
        RabbitMQ设置过期时间有两种:
            - 针对某一个队列设置过期时间 ;队列中的所有消息在过期时间到之后,如果没有被消费则被全部清除
            - 针对某一个特定的消息设置过期时间;队列中的消息设置过期时间之后,如果这个消息没有被消费则被清除。
            需要指出的是,针对特定消息设置时间,是指从这个消息在队列头部开始计时.
        
        TTL演示:
        创建配置类,配置队列/交换机,绑定的信息,指定消息过期时间
            package com.it.config;

            import org.springframework.amqp.core.*;
            import org.springframework.context.annotation.Bean;
            import org.springframework.context.annotation.Configuration;


            @Configuration
            public class TtlConfig {

                //创建过期队列
                @Bean
                public Queue createqueuettl1(){
                    //设置队列过期时间为10000 10S钟
                    return QueueBuilder.durable("queue_demo02").withArgument("x-message-ttl",10000).build();
                }

                //创建交换机

                @Bean
                public DirectExchange createExchangettl(){
                    return new DirectExchange("exchange_direct_demo02");
                }

                //创建绑定
                @Bean
                public Binding createBindingttl(){
                    return BindingBuilder.bind(createqueuettl1()).to(createExchangettl()).with("item.ttl");
                }
            }
        模拟发送消息,10秒后,队列中的消息会自动清除:    
            @RequestMapping("/send3")
            public String send3() {
                //设置回调函数
                //发送消息
                rabbitTemplate.convertAndSend("exchange_direct_demo02", "item.ttl", "hello ttl哈哈");
                return "ok";
            }
            
      
    
            

 

死信队列:
  死信队列原理图:
    

 

 


成为死信的三种条件:
  队列消息长度到达限制;
  消费者拒接消费消息,并且该消息没有重回队列;
  原队列存在消息过期设置,消息到达超时时间未被消费;

延迟队列:
  延迟队列原理图:

    

 延迟队列的典型应用场景:

  典型的案例:下订单之后,30分钟如果还未支付则,取消订单回滚库存。

posted @ 2020-04-06 17:07  moonlighter  阅读(291)  评论(0编辑  收藏  举报