九、SpringBoot与消息

一、消息队列作用:

提升系统异步通信、扩展解耦能力;

1.异步通信示意:

  1. 同步消息
用户 注册信息写入数据库 发送注册邮件 发送注册短信 50ms 50ms 50ms 响应150ms 用户 注册信息写入数据库 发送注册邮件 发送注册短信
  1. 并发消息
用户 注册信息写入数据库 发送注册邮件 发送注册短信 50ms 50ms 50ms 响应100ms 用户 注册信息写入数据库 发送注册邮件 发送注册短信
  1. 异步消息
用户 注册信息写入数据库 写入消息队列 发送注册邮件 发送注册短信 50ms 5ms 50ms 50ms 响应55ms 用户 注册信息写入数据库 写入消息队列 发送注册邮件 发送注册短信

2、应用解耦

调用库存接口
写入
订阅
订单系统
库存系统
订单系统
消息队列
库存系统

3、流量削峰

写入消息队列
根据规则读取秒杀请求
用户请求
消息队列
秒杀业务处理

二、消息服务中两个重要概念:

当消息发送者发送消息后,将由消息代理接管,消息代理保证消息传递到指定目的地;
两种形式目的地:

  1. 队列(queue):点对点消息通信(point-to-point)
  2. 主题(topic):发布(publish)- 订阅(subscribe)消息通信

1、点对点式

  1. 消息发送者发送消息,消息代理将其放入一个队列中,消息接受者从队列获取消息内容,消息读取后被移出队列;
  2. 消息只有唯一的发送者和接受者,单并不是说只有一个接受者;

2、发布订阅式

  1. 发送者发送消息到主题,多个接受者监听这个主题,那么就会在消息到达时同时收到消息;

三、JMS & AMQP

  1. JMS(Java Message Service):基于JVM消息代理的规范。ActiveMQ、HornetMQ是JMS实现;
  2. AMQP(Advanced Message Queuing Protocol):高级消息队列协议,也是一个消息代理的规范,兼容JMS。RabbitMQ是AMQP的实现。
  3. 区别:
JMSAMQP
定义java api网络线级协议
跨语言
跨平台
Model提供两种消息模型:
(1)Peer-2-Peer
(2)Pub/Sub
提供了五种消息模型:
(1)direct exchange
(2)fanout exchange
(3)topic exchange
(4)headers exchange
(5)system exchange
本质来讲,后四种和JMS的pub/sub模型没有太大差别,仅在路由机制上做了更详细的划分
支持消息类型多种消息类型:
TextMessage
MapMessage
BytesMessageStreamMesage
ObjectMessage
Message(只有消息头和属性)
byte[]
当实际应用时,有复杂的消息,可以将消息序列化后发送
综合评价JMS定义了JAVA API层面的标准;在JAVA体系中,多个client均可以通过JMS平台进行交互,不需要修改代码,但是对跨平台的支持较差。AMQP定义了wire-level层的协议标准;天然具有跨平台、跨语言特性。

四、Spring & SpringBoot支持

1、Spring支持

  • spring-jms提供了对JMS的支持;
  • spring-rabbit提供了对AMQP的支持;
  • 需要ConnectionFactory的实现来连接消息代理;
  • 提供JMSTemplate、RabbitTemplate来发送消息;
  • @JMSListener(JMS)、@RabbitListener(AMQP)注解在方法上监听消息代理发布消息;
  • @EnableJms@EnableRabbit开启支持;

2、SpringBoot支持自动配置

  • JmsAutoConfiguration
  • RabbitAutoConfiguration

下面以RabbitMQ为例介绍消息队列在SpringBoot中的应用。

五、RabbitMQ

RabbitMQ是由erlang开发的一个AMQP的开源实现;

1、核心概念

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

在这里插入图片描述

2、RabbitMQ运行机制

  1. 点对点:消息中的路由键如果和Binding中的binding key 一致,交换器就将消息发送到对应的队列中。在这里插入图片描述
  2. Fanout Exchange:每个发送到fanout类型交换器的消息都会分到所有绑定的队列上去;
    在这里插入图片描述
  3. topic exchange:topic交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配;两个通配符:
    • ‘#’:匹配0个或者多个单词;
    • ‘*’:匹配一个单词;
      在这里插入图片描述

3、RabbitMQ的安装测试

1.基于docker安装RabbitMQ

  1. 拉取镜像:
    docker pull rabbitmq:3.9.13-management
    
  2. 查看镜像:
    docker ps
    
  3. 启动镜像:
     docker run -d -p 5672:5672 -p 15672:15672 --name myRabbitMQ 1fb410f20779
    
  4. 查看镜像启动情况:
    docker ps
    
  5. web页面访问:http://Linux-ip:15672/ 默认用户名:guest,密码:guest
    在这里插入图片描述

2.常用的三种exchange测试:

  1. 添加exchange
    在这里插入图片描述
  2. 添加queue
    在这里插入图片描述
  3. 添加Binding
    在这里插入图片描述
    同理,我们为fanout和topic的exchange绑定queue
    在这里插入图片描述
    在这里插入图片描述
  4. 测试
    在这里插入图片描述
    查看消息
    在这里插入图片描述
    获取消息结果:
    在这里插入图片描述

4、SpringBoot和RabbitMQ的整合

自动配置原理:

	1.自动配置类:RabbitAutoConfiguration
	2.自动配置连接工厂: rabbitConnectionFactory
	3.RabbitProperties 封装了 RabbitMQ 的配置
  1. 引入maven依赖:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    
  2. application.yml配置
    spring:
      rabbitmq:
        host: 192.168.1.132
        username: guest
        password: guest
        port: 5672
        virtual-host: /
    
  3. 测试RabbitMQ
    • AMQPAdmin:RabbitMQ系统管理功能组件,可用于创建和删除queue、exchange、binding
          @Autowired
          private AmqpAdmin amqpAdmin;
      
          @Test
          void createExchange(){
              //创建exchange
              amqpAdmin.declareExchange(new DirectExchange("amqpadmin.exchange"));
              //创建queue
              amqpAdmin.declareQueue(new Queue("amqpadmin.queue"));
              //创建绑定
              amqpAdmin.declareBinding(
                      new Binding(
                              "amqpadmin.queue", 
                              Binding.DestinationType.QUEUE,
                              "amqpadmin.exchange",
                              "amqpadmin.queue",
                              null)
              );
          }
      
    • RabbitTemplate:给RabbitMQ发送和接收消息
          @Autowired
          private RabbitTemplate rabbitTemplate;
          
          /**
           * 1.单播(点对点)
           */
          @Test
          void contextLoads() {
              //Message需要自己构造一个,定义消息内容和消息头
              //rabbitTemplate.send(exchange,routingKey,message);
      
              //object默认当成消息体,只需要传入要发送的对象,自动序列化发送给rabbitMq
              //rabbitTemplate.convertAndSend(exchange,routingKey,object);
              Map<String,Object> map = new HashMap<>();
              map.put("message","这是第一个消息");
              map.put("data", Arrays.asList("helloworld",123,true));
              //对象被默认序列化后发送出去
              rabbitTemplate.convertAndSend("exchange.direct","hymll.news",map);
          }
      
          //接收数据,do
          @Test
          void receive(){
              final Object o = rabbitTemplate.receiveAndConvert("hymll.news");
              System.out.println("o.getClass() = " + o.getClass());
              System.out.println("o = " + o);
          }
      
          @Test//测试发布订阅模式
          void sendMessageFanout(){
              rabbitTemplate.convertAndSend("exchange.fanout","",new Book("西游记","吴承恩"));
          }
      
          //发送一个Book对象消息
          @Test
          void sendMessage(){
              rabbitTemplate.convertAndSend("exchange.direct","hymll.news",new Book("西游记","吴承恩"));
          }
      
  4. 修改消息序列化
    默认使用SimpleMessageConverter(JDK默认序列化方式)的序列化方式:
    在这里插入图片描述
    我们可以修改其他序列化方式,比如JSON
    在这里插入图片描述
    代码:
    @Configuration
    public class MyAMQPConfig {
    
        @Bean
        public MessageConverter messageConverter(){
            return new Jackson2JsonMessageConverter();
        }
    }
    
  5. 设置RabbitMQ监听
    通常情况下,我们需要一直监听RabbitMQ的消息进行消费,SpringBoot使用@EnableRabbit+ @RabbitListener注解简化了监听方法。
    • 在主类上开启RabbitMQ的注解;
      @EnableRabbit //开启基于注解的RabbitMQ
      @SpringBootApplication
      public class Springboot02AmqpApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(Springboot02AmqpApplication.class, args);
          }
      
      }
      
    • 开启监听
      @Service
      public class BookService {
      
          @RabbitListener(queues = "hymll.news")
          public void receive(Book book){
              System.out.println("收到消息:" + book);
          }
      
          @RabbitListener(queues = "hymll")
          public void receive02(Message message){
              System.out.println("messagegetBody = " + message.getBody());
              System.out.println("MessageProperties = " + message.getMessageProperties());
          }
      }
      
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天使吻过的BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值