什么是消息队列? Springboot整合rabbitMQ

一、消息介绍:    JMS和AMQP规范

    
  • 消息中间件

 
  • 什么是同步和异步模式?

    举个例子:普通B/S模式(同步)AJAX技术(异步)
        同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
        异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->慢慢等待处理完毕
 
    同步,就是你和别人聊天,你回了消息后你就要一直等待他发消息,直到你们两个不再联系为止,在此期间不能做任何事情
    异步,你等待对面发送消息的期间可以做其他事情,例如吃饭、打游戏等。等到吃完饭或游戏打完后慢慢回消息。
    
  • 什么是消息中间件?

        消息中间件:是利用高效可靠的消息传递机制进行异步的数据传输,并基于数据通信进行分布式系统的集成。通过提供消息队列模型和消息传递机制,可以在分布式环境下扩展进程间的通信。
        中间件是帮助应用程序与其他应用程序、网络、硬件、操作系统交互或通信的软件(将业务和底层逻辑解耦)。
 
  • 消息中间件(默认JMS)一般有两种传递模式:

        点对点模式:消息生产者将消息发送到队列中,消息消费者从队列中接收消息,消息生产者只能有一个,而消息消费者可以有多个,消息一旦被某一个消费者消费后就会被在队列中移出,其他消费者就不能再收到该消息(即只能有一个收到消息)。消息可以在队列中进行异步传输。
        发布/订阅模式:发布订阅模式是通过一个内容节点来发布和订阅消息,这个内容节点称为主题(topic或channel),消息发布者将消息发布到某个主题,消息订阅者订阅这个主题的消息,主题相当于一个中介。主题是的消息的发布与订阅相互独立,不需要进行基础即可保证消息的传递,发布/订阅模式在消息的一对多广播是采用(即所有的消息消费者都能收到消息)。
    
  • 消息中间件可以做什么?

        1、应用程序之间不采取直接通信,而是使用消息中间作为中介,做到数据的异步通信。开发人员不需要考虑网络协议和远程调用的问题,只需要通过各消息中间件所提供的api,就可以简单的完成消息推送,和消息接收的业务功能。
        2、消息的生产者将消息存储到队列中,消息的消费者不一定马上消费消息,可以等到自己想要用到这个消息的时候,再从相应的队列中去获取消息。这样的设计可以很好的解决,大数据量数据传递所占用的资源,使数据传递和平台分开,不再需要分资源用于数据传输,可以将这些资源用去其他想要做的事情上。
    
  • 消息队列的两个规范

        JMS和AMQP两种规范的消息队列:

            JMS:Java消息服务:
                j2ee提供的基于JVM消息代理的规范。 ActiveMQ、HornetMQ是JMS实现的消息中间件
 
            AMQP:高级消息队列协议:
                也是一种消息代理规范,兼容JMS。 RabbitMQ是AMQP实现的消息中间件
    
  • 两种规范的区别

                                            

    
  • Spring的支持:

                                         

    
    

二、RabbitMQ介绍

 
  • 什么是RabbitMQ?

        RabbitMQ是AMQP规范的的一个实现产品。(在 高级-消息)
    
  • RabbitMQ核心概念:

    Message
        消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
 
    Publisher
        消息的生产者,也是一个向交换器发布消息的客户端应用程序。
 
    Exchange
        交换器,用来接收生产者发送的消息并将这些消息路由(下面讲解)给服务器中的队列。
        Exchange有4种类型:direct(默认),fanout, topic, 和headers,不同类型的Exchange转发消息的策略有所区别
 
    Queue
        消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,排队等待消费者连接到这个队列将其取走。
 
    Binding
        绑定,用于消息队列Queue和交换器Exchange之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
        Exchange 和 Queue 的绑定可以是多对多的关系。
 
    Connection
        网络连接,比如一个TCP连接。
 
    Channel
        信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
 
    Consumer
        消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
 
    Virtual Host
        虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,
        RabbitMQ 默认的 vhost 是 / 。
 
    Broker
        表示消息队列服务器实体

     

AMQP中的消息路由

    AMQP比JMS中增加了Exchange和Binding角色。生产者将消息发布给Broker(服务器),然后给到一个Exchange(交换机)上,消息最终到达队列并被消费者接收,而Binding决定交换器的消息应该到哪个队列上。 
    交换器和Binding不同消息到达队列的规则就不一样。Exchange有4中,前三中是点对点,后三种是发布/订阅(最后一个不常用)
 

Exchange的4种消息的分发策略:  direct(默认),fanout, topic, 和headers

 
    direct:消息中的路由键(routing key)如果和Binding中的 binding key 完全一致,交换器就将消息发布到对应的队列中(单播通信,点对点)。
                 
 
    fanout:当接收到消息后,不管消息的路由键是什么,他都会将消息给他下面绑定的所有队列都发一份(广播,是速度最快的)。
                  
 
    topic:允许对消息中的路由键(routing key)的key做模糊匹配,将消息发送给匹配的key的队列。
            两头都支持两个通配符:"#"和"*": 
                #匹配0或多个单词;*匹配一个单词
                 
    
    headers:匹配的是消息中的header而不是路由键,他和direct完全一致,但是性能差很多,几乎用不到了
 
  
 

三、RabbitMQ安装测试: windows也类似。

 
  • 安装RabbitMQ: docker

        下载安装带有management版本的。这些带有web的管理界面: docker pull rabbitmq:3.8.7-management
        
  • 运行docker:

        客户端端口:5672
        管理界面管理web页面的端口:15672
    docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq 容器id
    
  • 打开网页: 用户名密码都是guest

                   
 
  • 具体操作RabbitMQ:    按照下面的图创建消息交换器和队列:研究exchange的4种类型的特点

                  
 
  • 创建Exchange交换器:

                 

 
  • 创建queues消息队列:

                 
        
  • 绑定交换器和消息队列: 将上面创建的三个交换器全部绑定点击的四个队列。

    • 路由键和绑定的队列queue值相同(这里队列的路由键会和消息里的路由键匹配)
    • 不过topic的路由键routing key 指定为: xxx.#、*.xxx、#.xxx等(#0到多个单词,*一个单词)
                         
                   
    
  • 开始发送消息: 分别测试每个Exchang类型发送不同的消息,发送之后去队列里面查看接收情况

                 
    
    direct: 点对点通信,给路由键完全一致的队列发送消息。当消息被消费者接收后就消失。
    fanout: 广播通信,和路由键无关。所有绑定的队列都会收到消息。
    topic: 模糊匹配,因为是*和#所以匹配的所有队列都会收到消息
 
  • 接收消息:

                   
 
 
 

四、整合RabbitMQ:    

                
 
  • pom:

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

 

  • 打开RabbitMq的自动配置类:RabbitAutoConfiguration

        
在第二个RabbitTemplaConfiguration中注册了以下bean:
        rabbitTemplateConfigurer: 将配置文件的数据转换为RabitMQ能使用的数据
        RabbitTemplate:等价于Redis的RedisTemplate,重要的bean
        AmqpAdmin: RabbitMQ的系统管理组件,不用来发送消息和接收消息,他用来创建一个队列,创建一个交换器等,这样就不用手动创建了。
 
  • 配置连接:   

spring:
    rabbitmq:
        addresses: 192.168.2.111  #默认localhost
        username: guest      #用户名密码都是guest
        password: guest
        port: 5672          #默认5672
        virtual-host: /     #默认/

 

  • 测试发送和接收消息: 先解决乱码问题! 后面写了。无论单播还是广播,只要发送给对应的交换器Exchange就会自动转换!

 
package com.springboot.msg;

import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.HashMap;

@SpringBootTest
class Springboot12AmqpApplicationTests {

    //注入RabbitMQ
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void direct(){
        /**
        *     测试单播,点对点: direct
        *        发送消息:  send、convertAndSend
        *        接收消息:  receiveAndConvert
        */

        /*
            发送主要使用下面两个方法: 一般都使用第二种

                // Message需要自己定义消息体内容和消息头
                rabbitTemplate.send(exchange, routingKey, Message message);

                //Object默认当做消息体,只需要传入要发送的对象,自动序列化发送给 RabbitMQ
                rabbitTemplate.convertAndSend(exchange, routingKey, Object message); //参数对应创建的管理界面中写
            
            接收消息:
                

        */

        //发送消息:
        HashMap<String , Object> map = new HashMap<>();
        map.put("msg","第一个参数");
        map.put("data","number2");

        //无论单播还是广播,只要将消息发送给对应的交换器就会自动转换
        rabbitTemplate.convertAndSend("exchange.direct","atguigu.news",map);


        // 接收数据
        Object o = rabbitTemplate.receiveAndConvert("atguigu.news");
        
        // 数据能够准确的转义过来。 并且消息队列里的该数据也就不存在了
        System.out.println(o.getClass());  //class java.util.HashMap
        System.out.println(o);  //{msg=第一个参数, data=number2}
    }


        @Test
        public void fanout(){
         /**
         *    测试广播: 广播的交换器是: exchange.fanout
         *     所有队列都能收到!
         */

            ArrayList<Object> list = new ArrayList<>();
            list.add("张三");
            list.add("23岁");
            rabbitTemplate.convertAndSend("exchange.fanout","和key无关",list);
        }
    }

 

  •   查看是否接收到信息: 虽然乱码了! 但是收到了。 

                  
 
  • 解决乱码: 数据是通过转换器转换数据的

// 在RabbitTemplate里面的 208行左右有: 其中指定了序列化的方式,但是我们当然希望数据以 JSON 的格式传输。因此要自己定义并覆盖!
 private MessageConverter messageConverter = new SimpleMessageConverter();
    
    • 使用Jackson: 推荐,不过每次发送消息都要调用
 HashMap<String , Object> map = new HashMap<>();
        map.put("msg","第一个参数");
        map.put("data","number2");
            
        ObjectMapper objectMapper = new ObjectMapper();
        String m = objectMapper.writeValueAsString(map);

        rabbitTemplate.convertAndSend("exchange.direct","atguigu.news",m);
    
    • 自定义MessageConverter: 无论哪次发送消息后都不用再管了
                这是MessageConverter的所有实现类,默认是蓝色的Simplexxx,这里要使用Json的。
                
     
   import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
        import org.springframework.amqp.support.converter.MessageConverter;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;

        @Configuration
        public class myAMQPConfig {

            @Bean
            public MessageConverter messageConverter(){ //别导错包 amqp 包下的,下面用转Json

                return new Jackson2JsonMessageConverter();
            }
        }
    
 
  • 监听消息:  其他系统或业务会监听消息队列中是否有消息入队,如果有,这些系统或业务等就进行某种操作

        如: 有订单系统和库存系统,这两个系统的数据都是通过消息队列进行交互。如果一个用户下了单就会将消息放入消息队列中,而库存系统就必须要实时监听消息队列中的消息,一旦有新的订单进来,库存系统就要进行库存相关的操作
 
        特点
            * 监听会把消息队列中的全部消息取出然后消费!即使消息中的数据类型是无法匹配参数类型的消息也会被消费!!
            * 如果参数类型是Object,会将消息的全部属性获取到!!(下面方框中的一大串数据都是)
            * 监听消费消息 的优先级高于 接收消息的方法 (rabbitTemplate.receiveAndConvert("路由键");)  监听会先消费消息
  • 在配置类上添加 @EnableRabbit 开启基于注解的RabbitMQ配置

                    
 
  • 在一些Service层的方法上添加 @RabbitListener(queues={"xxx"})
    • 该方法开启监听 名字为xxx的消息队列。queues为数组类型,可以同时监听多个消息队列!
    • 如果插入队列的消息类型是Student类型,就会获取过来为监听的方法使用。如果不是,参数值就为null,并且取出该消息!
                    
第二个测试方法:Object参数获取消息的全部数据:
 
    obj: (Body:'{"msg":"第一个参数","data":"number2","student":{"id":1,"name":"张三"}}' MessageProperties [headers={__ContentTypeId__=java.lang.Object, __KeyTypeId__=java.lang.Object, __TypeId__=java.util.HashMap}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=exchange.direct, receivedRoutingKey=atguigu.news, deliveryTag=1, consumerTag=amq.ctag-aBFcC85DVd3CkiJ6kqhEDA, consumerQueue=atguigu.news])
    
  • AmqpAdmin:帮助管理队列和交换器。

    • 使用消息的时候并不一定都把所有的队列和交换器都创建好的,所以我们可以一边使用一边创建
    
    1、注入AmqpAdmin: 已经RabbitAutoConfiguration中自动配置好了(最上面有写)
    2、使用:都是amqpAdmin对象的方法。 具体方法和使用看源码更省事!!
        declareXxx: declare开头的都是创建一些组件
        delete/remove: 删除组件
    
        declareExchange(Exchange e): 创建交换器。参数是接口,具体实现可以指定创建什么类型的交换器(direct、fanout、topic)
                    Exchange的名字默认是随机的,可以通过参数指定名字。
            
        
        declareQueue(Queue q): 创建队列。Queue本身就是一个类,所以参数可以直接new Queue()。 两个参数的构造:(队列名字,是否持久化)
            
 
        Binding(queueName/exchangeName, <-Type, exchangeName, routingKey,Map<String, Object> arguments): 绑定队列和交换器。 
        第一个参数: 可以通过Exchange绑定Queue,也可以反过来绑定;
        第二个参数: 是第一个参数的类型,是Exchange就为Exchange类型,Queue就是Queue类型;
        第三个参数: 按理说第一个参数是Queue,第三个参数是Exchange;
        第四个参数: 现场指定的key;
        第五个参数: 一般为null;
                             
        全部方法:可以参考使用下面方法
                
    
    
 
 
        
    
 
    
    
    
 
        
    
 
    
    
    
    
    
 
 
    
     
    
 
    
    
    
 
    
    
    
    
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值