Spring Boot学习(十三):整合RabbitMQ

这里是一个学习过程笔记的汇总:Spring Boot学习汇总


1、RabbitMQ的简介

        MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取队列中的消息。而RabbitMQ是MQ的一种,是一个由erlang语言开发的AMQP的开源实现。

下面详细介绍一下RabbitMQ的基本概念:

Message: 

        -- 消息,消息时不具名的,它由消息头和消息体组成。 消息体是不透明的,而消息头由一系列的可选属性组成,这些属性包括routing-key(路由键),priority(相对其他消息的优先权) 等等。

Publisher:

        -- 消息的生产者,也是一个向交换器发布消息的客户端应用程序。

Exchange: 

        -- 交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。

        -- 交换器有四种类型:direct(默认),fanout, topic, 和headers(基本用不到), 不同类型的Exchange转发消息的策略有所区别。

                direct: 消息中的路由键(routing key)如果和Bindding中的binding key一致,交换器就将消息发送到相应的队列中。完全匹配,单播的模式。

                              

               fanout: 每个发到fanout类型交换器的消息都会分到所有绑定的队列上,并不处理路由键。fanout类型转发消息是最快的。

                              

                 topic:  topic交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。 模式会识别两个通配符:# 匹配0个或多个单词,  * 匹配一个单词

                                   

Queue:

        -- 消息队列,用来保存消息知道发送给消费者。它是消息的容器,也是消息的终点。一个消息可以投入一个或多个消息队列。消息一直在队列中,等待消费者连接这个队列将其取走。

Binding:

        -- 绑定,用来决定交换器的消息应该发送给哪个队列。

        -- Exchange和Queue的绑定可以是多对多的关系。

Connection:

        -- 网络连接,比如一个TCP连接。

Channel:

        -- 信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真是的TCP连接内的虚拟连接,AMQP命令都是通过信道发送出去的,不管是发布消息,订阅队列还是接收消息,这些动作都是通过信道完成的。因为对于操作系统来说建立和销毁TCP连接都是非常昂贵的开销,所以引入的信道的概念,以复用一条TCP连接。

Consumer:

        -- 消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

Broker:

        -- 表示消息队列服务器实体。其实Broker就是接收和分发消息的应用,也就是说RabbitMQ Server就是Message Broker。

Virtual Host:

        -- 虚拟主机,一个Broker里可以开有多个VirtualHost,它的作用是用作不同用户的权限分离。

        -- 表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个vhost本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列、交换器、绑定和权限机制。vhost是AMQP概念的基础,必须在连接时指定,RabbitMQ默认的vhost是 /

以下两证图,可以帮助理解以上各部分之间的关系:

点对点消息通信:

        -- 生产者生产消息后,消息代理将其投递到消息队列,消费者从消息队列中获取消息内容,消息读取后被移除队列。

        -- 消息只有唯一的正产者和消费者,但并不是说只能有一个接收者。(消息被多个接收者中的某一个接收者消费后就被移除队列,所以只有唯一的消费者)

发布订阅式通信:

        -- 生产者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达同时收到消息。

2、RabbitMQ的使用

RabbitMQ的下载安装参考这篇文章:Window10环境下安装RabbitMQ 

2.1、启动RabbitMQ, 访问:http://localhost:15672

2.2、管理后台测试

RabbitMQ管理后台的使用请参考这篇文章:RabbitMQ管理后台的使用

创建如下交换器:

        direct类型:exchange.direct, 

        fanout类型:exchange.fanout,

        topic类型:exchange.topic

          

创建消息队列:hangzhou,hangzhou.news,hangzhou.weather,suzhou.news  

              

创建交换器与所有队列之间的binding:

测试:

       在exchange.direct交换器上发送一条消息,路由键为hangzhou,如下:

    

     上面发送的消息的交换器是direct类型,路由键是hangzhou, 所以只有binding的交换器为direct类型并且路由键为hangzhou的队列才可以收到这条消息,即队列hangzhou,查看队列列表,如下:

查看杭州队列中的消息,如下,正式上面我们发送的消息:

            在exchange.fanout交换器上发送一条消息,我们也给设置一个路由键为hangzhou.news,如下:

  按照规则,fanout类型的交换器是不处理路由键的,该交换器发送的消息,会到达所有与该交换器绑定的队列上。如下,除了hanghzou队列的消息数为2,其他队列消息均为1:

  在topic类型的交换器发送一条消息,路由键设置为hangzhou.haha,如下:

   按照规则,topic类型的交换器发送的消息,是使用的模式匹配查找队列,查看上面的binding,可以看到该消息会到达hangzhou, hangzhou.news, hangzhou.weather三个队列,如下,这三个队列的消息数均增加1:

3、代码测试

3.1、创建一个springboot项目,引入Web,RabbitMQ模块:

3.2、查看自动配置

老生常谈,先看下自动配置类,即RabbitAutoConfiguration

可以看到,给我们配置了连接工厂ConnectionFactory,

连接工厂的相关数据是从RabbitProperties中获取的:

如上的  ip,端口号,用户名以及密码都默认给我们配置好了,如果要修改的话,直接在主配置文件中进行修改即可。

还给容器注入了一个RabbitTemplate, 和前面讲到的RedisTemplate类似,用来操作Rabbit消息队列,发送消息和接收消息等等操作:

还给容器注入了一个AmqpAdmin,这是一个系统管理功能组件,用来给RabbitMQ创建exchange,创建queue,创建binding等等

3.3、测试

代码测试前我们将上面测试发送的消息全部清除:

不需要任何的配置,直接全部使用默认的配置,我们在测试类中进行测试,引入RabbitTemplate, 先测试点对点的单播模式:

package com.example.springbootactivemq;

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;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootActivemqApplicationTests {

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 发送消息有两个方法
     *     1、 rabbitTemplate.send(exchange, routeKey, message)
     *         传入交换器,路由键,然后消息Message,只不过是这个消息需要我们自己去构建,包括消息体内容和消息头
     *     2、rabbitTemplate.convertAndSend(exchange, routeKey, object)
     *         这个是我们常用的发送消息方式,只需要传入交换器,路由键,以及消息对象,该对象会自动转化为Message,不用我们自己构建了
     *         此处我们就用常用的convertAndSend()进行测试
     */
    @Test
    public void sendMessage() {
        // 单播(点对点),也就是交换器类型为direct的
        Map<String, Object> map = new HashMap<>();
        map.put("name", "pavel");
        map.put("hobby", Arrays.asList("basketball","climb","running"));
        // map对象被默认序列化以后发送出去
        rabbitTemplate.convertAndSend("exchange.direct", "hangzhou", map);
    }
}

运行,然后查看RabbitMQ管理后台:

队列hangzhou收到了一条消息,点进去看消息内容:

我们并看不懂,这是因为,传过来的对象默认使用java的序列化方式,待会儿再来处理这种情况,我们先来接收一下这个 消息:

/**
     *  接收消息也有两个方法
     *       1、rabbitTemplate.receive(queueName)
     *            直接传入队列名称,会给我们返回一个Message,包括了消息体和消息头
     *       2、rabbitTemplate.receiveAndConvert(queueName)
     *            这是常用的一种接收消息的方法,也是传入一个队列名称,会给我直接返回我们想要的Object对象
     */
    @Test
    public void receiveMessage() {
        Object message = rabbitTemplate.receiveAndConvert("hangzhou");
        System.out.println("接收的消息类型:" + message.getClass());
        System.out.println("接收的消息内容" + message);
    }

运行,看控制台输出:

现在再看消息队列hangzhou里面,这条消息就不存在了:

刚才上面的消息发送出去后,我们在总管理后台看到的消息是使用java默认序列化规则序列化之后的结果,根本看不懂,如果消息时以json格式序列化之后发送出去,这样多好,那么怎么实现呢?

我们先来看一下默认的消息发送是怎么序列化的:

RabbitTemplate类中有个消息转换器:

是由SimpleMessageConverter实现的,这个类间接实现了MessageConverter接口,类中有一个fromMessage方法,将消息体以红框中的规则进行序列化:

我们要想让发送的消息按照我们定义的方式序列化,那么我们就得自己定义转换规则,自己去定义MessageConvert,首先来看一下MessageConvert这个接口,Ctrl + H看一下这个接口有哪些实现类

可以看到有一个json相关的实现类,那么我们就按照Jakson2JsonMessageConverter转换器来序列化我们发送的消息。

创建一个配置类,如下:

package com.example.springbootactivemq.conofig;

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;

/**
 * @author pavel
 * @date 2018/11/30 0030
 */
@Configuration
public class MyAMQPConfig {

    @Bean
    public MessageConverter messageConverter () {
        return new Jackson2JsonMessageConverter();
    }
}

然后再测试发送消息,看管理后台:

以上是测试的单播模式,剩下的广播模式套路是一样,只是发送消息时定义的交换器是fanout和topic类型的。

4、消息监听

开发商城系统时会有这样一个场景,用户完成购物之后,商品的库存会做相应的减少,如果每次用户购物后就直接调用库存相关api进行库存数量的操作,这样是不现实的,此处引入消息队列,在用户完成购物之后,将订单信息发送到消息队列中,然后库存系统去监听这个消息队列, 一旦队列中有新的订单信息增加,那么就对库存做相应的操作。  此处应用的就是消息监听。

创建商品实体类:

package com.example.springbootactivemq.domain;

import java.io.Serializable;

/**
 * @author pavel
 * @date 2018/11/30 0030
 */
public class ProductModel implements Serializable {

    private static final long serialVersionUID = 5038371528848831641L;

    private Long id;
    private String name;
    private String origin;

    public ProductModel() {
    }

    public ProductModel(Long id, String name, String origin) {
        this.id = id;
        this.name = name;
        this.origin = origin;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getOrigin() {
        return origin;
    }

    public void setOrigin(String origin) {
        this.origin = origin;
    }

    @Override
    public String toString() {
        return "ProductModel{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", origin='" + origin + '\'' +
                '}';
    }
}

创建service,在需要监听的方法上加@RabbitListener注解,开启监听模式:

package com.example.springbootactivemq.service;

import com.example.springbootactivemq.domain.ProductModel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

/**
 * @author pavel
 * @date 2018/11/30 0030
 */
@Service
public class ProductService {

    // 加上@RabbitListener注解,就开启了监听模式
    // queues是一个数组,可以监听多个消息队列
    @RabbitListener(queues = "hangzhou")
    public void receive(ProductModel productModel){
        System.out.println("订单收到了,商品信息为:" + productModel);
    }
}

然后再启动类上添加注解@EnableRabbit,开启基于注解的RabbitMQ

package com.example.springbootactivemq;

import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// 开启基于注解的RabbitMQ
@EnableRabbit
@SpringBootApplication
public class SpringbootActivemqApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootActivemqApplication.class, args);
    }
}

然后测试,代码如下:

运行,可以看到控制台输出:

消息监听成功。

5、AmqpAdmin系统管理组件

5.1、创建exchange

用AmqpAdmin的declareExchange()方法,参数是需要创建的交换器对象,可以是下图右侧的几种:

创建一个direct类型的交换器:

运行,看管理控制台:

然后创建队列,直接调用AmqpAdmin的declareQueue(), 第一个参数是队列名称,还可以有第二个参数:是否是持久化的:

运行,看管理后台:

创建绑定规则,binding:

Binding对象的各个参数详解:

运行,然后看管理控制台,添加binding成功:

然后自己可以按照上面的第三步测试一下发送消息和接收消息。

好了,关于Spring Boot与RabbitMQ整合的学习就先记录这么多,后期有更深入的学习体会再更上。

发布了34 篇原创文章 · 获赞 43 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/pavel101/article/details/84634026