SpringBoot2.x学习之路(五)RabbitMQ的使用

今天小七给大家介绍一下在Spring Boot项目中如何使用RabbitMQ,下面直入正题吧。

(一)RabbitMQ的安装以及介绍

之前的博文,小七有介绍过RabbitMQ以及如何安装,这里就不再赘述了,请查看下面的博文地址:
https://blog.csdn.net/qiyongkang520/category_6751853.html

(二)RabbitMQ的依赖引入

pom.xml中添加如下依赖即可:

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

(三)RabbitMQ的文件配置

.yml文件中添加如下配置:

spring:
  rabbitmq:
    host: 172.16.3.108
    port: 5672
    username: admin
    password: admin123
    virtualHost: cn

这里virtualHost可以不配置,就表示所有的交换机和队列都在/下创建。

(四)交换机的简单说明

目前RabbitMQ提供了direct、topic、headers、fanout四种交换机类型,每种类型都有自己的特点,大家可以根据具体的业务需求来选择使用,下面小七先简单介绍一下。
首先大家都知道,咱们应用系统给RabbitMQ发消息,不是直接发给队列,而是发给交换机,然后交换机根据自己的规则(路由key、键值对属性等)来分发到相应的队列上,也就是说交换机和队列之间是有绑定关系的,分别对应如下:

  • direct:交换机和队列是通过路由Key来匹配,并且是完全匹配;
  • topic:交换机和队列是通过路由Key来匹配,但是支持通配符匹配,也就是说给此类型交换机发消息时指定的路由Key如果满足多个路由的通配符匹配,那么这些队列都可以收到消息;
  • headers:交换机和队列是通过键值对来任一匹配或者完全匹配
  • fanout:交换机和队列只要绑定了即可,不需要指定任何规则,这些队列都可以收到消息

下面小七会一一介绍并截图,就容易理解多了。

(五)Direct交换机的使用

在Spring Boot怎么创建交换机、队列以及绑定呢,其实很简单,这里小七介绍两种方式,一是在配置类中创建,二是在接收消息监听类中创建。
这里小七先统一使用第二种方式,后面会介绍第一种方式。
小七,先一次性贴出监听类的所有代码MQReceiver.java:

package org.qyk.springboot.rabbitmq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

/**
 * <Change to the actual description of this class>
 *
 * @version 1.0
 * <pre>
 * Author       Date            Changes
 * yongkang.qi   2020年04月08日   Created
 *
 * </pre>
 * @since 1.7
 */

@Component
public class MQReceiver {
    private Logger logger = LoggerFactory.getLogger(MQReceiver.class);
    // EXCHANGE
    final static String EXCHANGE_DIRECT_EXCHANGE = "QYK_DIRECT_EXCHANGE";
    final static String EXCHANGE_TOPIC_EXCHANGE = "QYK_TOPIC_EXCHANGE";
    final static String EXCHANGE_HEADER_EXCHANGE = "QYK_HEADER_EXCHANGE";
    final static String EXCHANGE_FANOUT_EXCHANGE = "QYK_FANOUT_EXCHANGE";

    final static String EXCHANGE_SEND_TOPIC_EXCHANGE = "QYK_SEND_TOPIC_EXCHANGE";
    final static String EXCHANGE_RECEIVE_DIRECT_EXCHANGE = "QYK_RECEIVE_DIRECT_EXCHANGE";

    // QUEUE
    final static String QUEUE_DIRECT_MESSAGE1_APP1 = "QYK_DIRECT_MESSAGE1.APP1";
    final static String QUEUE_DIRECT_MESSAGE2_APP1 = "QYK_DIRECT_MESSAGE2.APP1";
    final static String QUEUE_DIRECT_MESSAGE3_APP1 = "QYK_DIRECT_MESSAGE3.APP1.AUR";
    final static String QUEUE_DIRECT_MESSAGE4_APP1 = "QYK_DIRECT_MESSAGE4.APP1.AUR";

    final static String QUEUE_TOPIC_MESSAGE1_APP1 = "QYK_TOPIC_MESSAGE1.APP1";
    final static String QUEUE_TOPIC_MESSAGE2_APP1 = "QYK_TOPIC_MESSAGE2.APP1";
    final static String QUEUE_TOPIC_MESSAGE3_APP1 = "QYK_TOPIC_MESSAGE3.APP1";

    final static String QUEUE_HEADER_MESSAGE1_APP1 = "QYK_HEADER_MESSAGE1.APP1";
    final static String QUEUE_HEADER_MESSAGE2_APP1 = "QYK_HEADER_MESSAGE2.APP1";

    final static String QUEUE_FANOUT_MESSAGE1_APP1 = "QYK_FANOUT_MESSAGE1.APP1";
    final static String QUEUE_FANOUT_MESSAGE2_APP1 = "QYK_FANOUT_MESSAGE2.APP1";

    // ROUTE KEY
    final static String ROUTE_KEY_CNR = "#.CNR";
    final static String ROUTE_KEY_USR = "#.USR";
    final static String ROUTE_KEY_EUR = "#.EUR";
    final static String ROUTE_KEY_AUR = "#.AUR";

    // DIRECT: Exchange、Queue、QueueBinding
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_DIRECT_MESSAGE1_APP1),
            exchange = @Exchange(value = EXCHANGE_DIRECT_EXCHANGE),
            key = QUEUE_DIRECT_MESSAGE1_APP1
    ))
    public void process1(String message) {
        logger.error("MqReceiver [QYK_DIRECT_MESSAGE1.APP1]: {}", message);
    }

    // DIRECT: Exchange、Queue、QueueBinding
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_DIRECT_MESSAGE2_APP1),
            exchange = @Exchange(value = EXCHANGE_DIRECT_EXCHANGE),
            key = QUEUE_DIRECT_MESSAGE2_APP1
    ))
    public void process2(String message) {
        logger.error("MqReceiver [QYK_DIRECT_MESSAGE2.APP1]: {}", message);
    }


    // TOPIC: Exchange、Queue、QueueBinding
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_TOPIC_MESSAGE1_APP1),
            exchange = @Exchange(value = EXCHANGE_TOPIC_EXCHANGE, type ="topic"),
            key = {ROUTE_KEY_CNR}
    ))
    public void process3(String message) {
        logger.error("MqReceiver [QUEUE_TOPIC_MESSAGE1_APP1]: {}", message);
    }

    // TOPIC: Exchange、Queue、QueueBinding
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_TOPIC_MESSAGE2_APP1),
            exchange = @Exchange(value = EXCHANGE_TOPIC_EXCHANGE, type ="topic"),
            key = {ROUTE_KEY_USR,ROUTE_KEY_EUR,ROUTE_KEY_AUR}
    ))
    public void process4(String message) {
        logger.error("MqReceiver [QUEUE_TOPIC_MESSAGE2_APP1]: {}", message);
    }

    // TOPIC: Exchange、Queue、QueueBinding
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_TOPIC_MESSAGE3_APP1),
            exchange = @Exchange(value = EXCHANGE_TOPIC_EXCHANGE, type ="topic"),
            key = {ROUTE_KEY_CNR,ROUTE_KEY_USR}
    ))
    public void process5(String message) {
        logger.error("MqReceiver [QUEUE_TOPIC_MESSAGE3_APP1]: {}", message);
    }

    // Headers:
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_HEADER_MESSAGE1_APP1),
            exchange = @Exchange(value = EXCHANGE_HEADER_EXCHANGE, type ="headers"),
            arguments = {@Argument(name = "One",value = "A"), @Argument(name = "Two",value = "B"), @Argument(name = "x-match",value = "any")}
    ))
    public void process6(String message) {
        logger.error("MqReceiver [QUEUE_HEADER_MESSAGE1_APP1]: {}", message);
    }

    // Headers:
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_HEADER_MESSAGE2_APP1),
            exchange = @Exchange(value = EXCHANGE_HEADER_EXCHANGE, type ="headers"),
            arguments = {@Argument(name = "One",value = "A"), @Argument(name = "Two",value = "B"), @Argument(name = "x-match",value = "all")}
    ))
    public void process7(String message) {
        logger.error("MqReceiver [QUEUE_HEADER_MESSAGE2_APP1]: {}", message);
    }

    // Fanout:
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_FANOUT_MESSAGE1_APP1),
            exchange = @Exchange(value = EXCHANGE_FANOUT_EXCHANGE, type ="fanout")
    ))
    public void process8(String message) {
        logger.error("MqReceiver [QUEUE_FANOUT_MESSAGE1_APP1]: {}", message);
    }

    // Fanout:
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(QUEUE_FANOUT_MESSAGE2_APP1),
            exchange = @Exchange(value = EXCHANGE_FANOUT_EXCHANGE, type ="fanout")
    ))
    public void process9(String message) {
        logger.error("MqReceiver [QUEUE_FANOUT_MESSAGE2_APP1]: {}", message);
    }



    // 交换机绑定交换机
    @RabbitListener(queues = QUEUE_DIRECT_MESSAGE3_APP1)
    public void process10(String message) {
        logger.error("MqReceiver [QYK_DIRECT_MESSAGE3_APP1]: {}", message);
    }

    // 交换机绑定交换机
    @RabbitListener(queues = QUEUE_DIRECT_MESSAGE4_APP1)
    public void process11(String message) {
        logger.error("MqReceiver [QYK_DIRECT_MESSAGE4_APP1]: {}", message);
    }
}

目前只需要关注process1和process2。
下面,咱们再来创建一下个单元测试类,代码如下:

package org.qyk.springboot.rabbitmq;

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


/**
 * <Change to the actual description of this class>
 *
 * @version 1.0
 * <pre>
 * Author       Date            Changes
 * yongkang.qi   2020年04月08日   Created
 *
 * </pre>
 * @since 1.7
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class RabbitMQTest {

    @Autowired
    private AmqpTemplate rabbitTemplate;

    @Test
    public void testCreateQueueAndExchange() {
        System.out.println("***********");
        System.out.println("***********");
    }

    @Test
    public void testSendDirectMessage() throws InterruptedException {
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_DIRECT_EXCHANGE,
                MQReceiver.QUEUE_DIRECT_MESSAGE1_APP1, "hello");
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_DIRECT_EXCHANGE,
                MQReceiver.QUEUE_DIRECT_MESSAGE2_APP1, "hello");

        Thread.sleep(5000);
    }

    @Test
    public void testSendTopicMessage() throws InterruptedException {
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_TOPIC_EXCHANGE,
                "TOPIC.MESSAGE.CNR", "hello");

        Thread.sleep(3000);
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_TOPIC_EXCHANGE,
                "USR.TOPIC.MESSAGE", "hello");

        Thread.sleep(3000);
        System.out.println("***********");
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_TOPIC_EXCHANGE,
                "TOPIC.MESSAGE.EUR", "hello");

        Thread.sleep(5000);
    }

    @Test
    public void testSendHeadersMessage() throws InterruptedException {
        String msg = "hello";
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setHeader("One", "A");
        Message message = new Message(msg.getBytes(), messageProperties);
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_HEADER_EXCHANGE, null, message);
        Thread.sleep(3000);
        messageProperties = new MessageProperties();
        messageProperties.setHeader("One", "A");
        messageProperties.setHeader("Two", "B");
        message = new Message(msg.getBytes(), messageProperties);
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_HEADER_EXCHANGE, null, message);
        Thread.sleep(5000);
    }

    @Test
    public void testSendFanoutMessage() throws InterruptedException {
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_FANOUT_EXCHANGE, null, "hello");
        Thread.sleep(5000);
    }

    @Test
    public void testSendExchangeExchange() throws InterruptedException {
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_SEND_TOPIC_EXCHANGE, "TEST.CNR", "hello");
        Thread.sleep(3000);
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_SEND_TOPIC_EXCHANGE, "TEST.USR", "hello");
        Thread.sleep(3000);
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_SEND_TOPIC_EXCHANGE, MQReceiver.QUEUE_DIRECT_MESSAGE3_APP1, "hello");
        Thread.sleep(3000);
        rabbitTemplate.convertAndSend(MQReceiver.EXCHANGE_SEND_TOPIC_EXCHANGE, MQReceiver.QUEUE_DIRECT_MESSAGE4_APP1, "hello");
        Thread.sleep(5000);
    }
}

这里只需要关注testCreateQueueAndExchange和testSendDirectMessage两个单元测试方法。
首先,咱们来运行testCreateQueueAndExchange,然后看下RabbitMQ管理后台的页面,首先是交换机,截图如下:
在这里插入图片描述
然后,再来看下队列,如下图:
在这里插入图片描述
接下来,再点进去看一下和队列的绑定关系,截图如下:
在这里插入图片描述
这个相信大家能看懂,消息发送到此交互机后,是通过发消息时指定的Routing key和上面的路由规则完全匹配来决定发送到哪个队列的。
下面,咱们再来运行一下testSendDirectMessage,运行日志如下:
在这里插入图片描述
就是按咱们说的规则来的,大家可以把QUEUE_DIRECT_MESSAGE1_APP1和QUEUE_DIRECT_MESSAGE2_APP1这两个队列的路由key指定成一样的,然后再运行testSendDirectMessage,就会发现当路由匹配上时,这两个队列都会收到此条消息。

(六)Topic交换机的使用

这里只需要关注接收类的process3、process4以及process5,下面直接先运行testCreateQueueAndExchange,来看下控制台交换机的效果,截图如下:
在这里插入图片描述
通配符的含义,简单说一下:

  • *(星号)仅代表一个单词
  • #(井号)代表任意个单词
    也就是说只要发送消息给此交互机时,如果指定的路由key能匹配到以上的通配符路由规则,那么对应的队列就可以收到此条消息。
    下面,大家可以运行testSendTopicMessage测试看看,这里小七就不演示了。

(七)Headers交换机的使用

这里,只需要关注消息接收类的process6和process7,咱们可以直接运行testCreateQueueAndExchange,先来看下控制台交换机的样子,如下:
在这里插入图片描述
这里先说明一下x-match的作用:

  • any:发送消息给交换机时指定的键值对,只要有一对键值匹配上,此队列就可以收到消息
  • all:发送消息给交换机时指定的键值对,所有的键值对都必须匹配上,此队列才能收到消息。
    大家可以运行testSendHeadersMessage看一下效果,小七也不演示啦。

(八)Fanout交换机的使用

这里大家只需要关注process8和process9,这个交换机类型比较简单,就像订阅模式一下,只要订阅了就会收到消息。下面直接截图:
在这里插入图片描述
大家可以运行testSendFanoutMessage来看一下效果。

(九)交换机绑定交换机

上面咱们介绍的都是队列如何与交换机绑定的,其实,交换机也可以与交互机绑定,下面咱们来一起看看,这里小七通过配置的方式来创建交换机、队列以及绑定关系,配置类如下:

package org.qyk.springboot.rabbitmq;

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

/**
 * <Change to the actual description of this class>
 *
 * @version 1.0
 * <pre>
 * Author       Date            Changes
 * yongkang.qi   2020年04月08日   Created
 *
 * </pre>
 * @since 1.7
 */
@Configuration
public class RabbitMQConfig {

    @Bean(name = "directQueue1")
    public Queue directQueue1() {
        return new Queue(MQReceiver.QUEUE_DIRECT_MESSAGE1_APP1);
    }

    @Bean(name = "directQueue2")
    public Queue directQueue2() {
        return new Queue(MQReceiver.QUEUE_DIRECT_MESSAGE2_APP1);
    }

    @Bean(name = "directQueue3")
    public Queue directQueue3() {
        return new Queue(MQReceiver.QUEUE_DIRECT_MESSAGE3_APP1);
    }

    @Bean(name = "directQueue4")
    public Queue directQueue4() {
        return new Queue(MQReceiver.QUEUE_DIRECT_MESSAGE4_APP1);
    }

    @Bean(name = "sendTopicExchange")
    public TopicExchange sendTopicExchange() {
        return new TopicExchange(MQReceiver.EXCHANGE_SEND_TOPIC_EXCHANGE);
    }

    @Bean(name = "receiveDirectExchange")
    public DirectExchange receiveDirectExchange() {
        return new DirectExchange(MQReceiver.EXCHANGE_RECEIVE_DIRECT_EXCHANGE);
    }

    @Bean
    Binding bindingExchangeQueue1(@Qualifier("directQueue1") Queue queue, TopicExchange sendTopicExchange) {
        return BindingBuilder.bind(queue).to(sendTopicExchange).with(MQReceiver.ROUTE_KEY_CNR);
    }

    @Bean
    Binding bindingExchangeQueue2(@Qualifier("directQueue2") Queue queue, TopicExchange sendTopicExchange) {
        return BindingBuilder.bind(queue).to(sendTopicExchange).with(MQReceiver.ROUTE_KEY_USR);
    }

    @Bean
    Binding bindingExchangeExchange(DirectExchange receiveDirectExchange, TopicExchange sendTopicExchange) {
        return BindingBuilder.bind(receiveDirectExchange).to(sendTopicExchange).with(MQReceiver.ROUTE_KEY_AUR);
    }

    @Bean
    Binding bindingExchangeQueue3(@Qualifier("directQueue3") Queue queue, DirectExchange receiveDirectExchange) {
        return BindingBuilder.bind(queue).to(receiveDirectExchange).with(MQReceiver.QUEUE_DIRECT_MESSAGE3_APP1);
    }

    @Bean
    Binding bindingExchangeQueue4(@Qualifier("directQueue4") Queue queue, DirectExchange receiveDirectExchange) {
        return BindingBuilder.bind(queue).to(receiveDirectExchange).with(MQReceiver.QUEUE_DIRECT_MESSAGE4_APP1);
    }

}

咱们再来看下QYK_SEND_TOPIC_EXCHANGE交换机的绑定关系,如下图:
在这里插入图片描述
QYK_RECEIVE_DIRECT_EXCHANGE的,小七也截图出来,如下:
在这里插入图片描述
看到没有,QYK_RECEIVE_DIRECT_EXCHANGE不是队列,是交换机。那么当客户端发消息到QYK_SEND_TOPIC_EXCHANGE交换机时,如果指定的路由如果能匹配到QYK_RECEIVE_DIRECT_EXCHANGE上,之后是怎么处理的呢。其实很简单,再把此路由放到QYK_RECEIVE_DIRECT_EXCHANGE上继续路由匹配就行了。
大家可以运行testSendExchangeExchange来看一下效果,小七就不演示了。

(十)结语

上面,小七没有介绍如何给RabbitMQ发消息,首先引入按下面方式引入客户端即可:

@Autowired
    private AmqpTemplate rabbitTemplate;

然后,发现消息的时候,指定交换机、路由、键值对以及消息等即可。
好了,如果有不明白的童鞋,一定要亲自动手试试,就能明白啦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值