RabbitMQ是目前应用非常广泛的一种消息队列之一,今天就来简单说一下RabbitMQ的四种交换机

以及在管理页面中创建交换机、队列、绑定关系

一、RabbitMQ中的几个关键字的概念

开始讲之前先来简单描述一下RabbitMQ中的几个关键的概念:

Broker:可以简单的理解为安装了RabbitMQ服务的这台机器就可以称为一个Broker

Exchange:交换机,消息经由它,通过路由键来判断并决定把消息投递给哪个队列,它类似于一个路由器的角色

Queue:队列,最终将消息投递到队列中,由消费端监听队列进行消费

Binding:绑定关系,需要给交换机绑定队列,绑定时需要给一个路由键

Routingkey:路由键,交换机和队列进行绑定时,需要指定路由键或通配符路由键。交换机根据路由 键来决定消息投递到哪个或哪些队列

二、mq运行流程

使用RabbitMQ前,首先需要根据业务来创建交换机和队列,创建完成后需要给交换机绑定队列(交换机和队列可以是多对多的关系),绑定队列时要指定具体的路由键或者通配符路由键

当生产者发送一条消息的时候,需要指定交换机和路由键,消息到达Broker后先转给刚才指定的交换机,交换机再根据路由键来决定把消息投递给与自己绑定的哪一个或哪一些队列,最后再由消费端来监听这些队列,消费处理对应的消息

有了整体的概念后,接着来讲RabbitMQ的四种交换机,然后在管理页面中创建交换机、队列并绑定关系

三、RabbitMQ的四种交换机

最新版本的RabbitMQ有四种交换机类型,分别是:Direct exchange、Fanout exchange、Topic exchange、Headers exchange
1、Direct exchange—直接类型交换机

要求消息带的路由键和绑定的路由键完全匹配,这是一个完整的匹配。

比如一个队列A绑定到该交换机上的路由键是“abc”,则只有指定的路由键是“abc”的消息才被投递给队列A,其他的不会投递给队列A

2、Fanout Exchange—扇出类型交换机

只需要简单的将队列绑定到该类型交换机上,该类型的交换机绑定队列时可以不指定路由键(指定了好像也不生效,自己测试过)

当消息发送给该交换机后,它会将消息投递给与该交换机绑定的所有队列

很像广播,每台子网内的机器都会获得一份消息,Fanout交换机转发消息是最快的

3、Topic Exchange—主题类型交换机

将路由键和某模式进行匹配。此时队列需要绑定某一个模式上。符号#匹配0个或多个单词,符号 *匹配一个单词。

4、Headers Exchanges

这种不常用,可以选择性忽略

4.在管理页面中创建交换机、队列、绑定关系

首先需要搭建好RabbitMQ的环境,可以参考使用Docker快速安装部署RabbitMQ

搭建好后,在浏览器输入IP地址+端口号15672,来到登录页面,输入默认账号:guest 密码:guest

1、创建交换机

首先点击Exchange菜单,然后在下边的Add a new exchange 里输入要新创建的交换机信息

输入名称,选择类型,选择类型的时候点开有4种类型,就是咱们上边讲到的那4种,最后点Add exchange,一个新的交换机就创建成功了

我把上边讲的3种常用类型的交换机都进行了创建

java rabbitmq 交换机绑定 rabbitmq交换机绑定队列问题_中间件

2、创建队列

点击Queues菜单,在下边的Add a new queue里输入要新创建的队列信息

java rabbitmq 交换机绑定 rabbitmq交换机绑定队列问题_中间件_02

3、创建绑定关系

在交换机列表点击对应的交换机,进入绑定页面

我给3个换机都绑定了对应的队列,如下图所示

java rabbitmq 交换机绑定 rabbitmq交换机绑定队列问题_发送消息_03

java rabbitmq 交换机绑定 rabbitmq交换机绑定队列问题_java rabbitmq 交换机绑定_04

五、发送消息测试

发送消息来测试一下
1、测试给Direct类型交换机发送消息

在交换机列表进入对应的交换机

输入对应的路由键Routing key和消息内容,点击发送

测试一条路由键为China的消息,根据我们建立的绑定关系,应该只有China这个队列能收到这条消息

可以看到,确实只有China这个队列能收到这条消息

收到消息后,我们可以点击对应的队列进去,获取刚才发的消息

点击Get Message(s)后,可以就可以获取到刚才发的消息了

2、测试给Fanout类型交换机发送消息

和上边的流程一样,我们给Fanout类型交换机发送消息,根据建立的绑定关系,所有和Fanout类型交换机绑定的对应都应该收到消息

如上图,可以看到和预期的一样所有和Fanout类型交换机绑定的对应都应该收到消息
3、测试给Topic类型交换机发送消息

测试发送一条路由键为China.news的消息,根据我们建立的绑定关系和绑定关系中的通配符路由键判断

这条消息3个队列应该都可以收到

六、代码示例

1.生产者

package com.cloud.mq.test5;

import com.cloud.mq.utils.RabbitMqUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.util.Scanner;

/**
 * @author liuYuHao
 * @create 2021/12/23 15:38
 */
public class Producer5 {

    //队列的名字
    private final static String EXCHANGE_NAME = "testExchange";

    //发送消息
    public static void main(String[] args) throws Exception {

        Channel channel = RabbitMqUtil.getChannel();

        /**
         * 声明交换机
         * 1.交换机名字
         * 2.交换机类型
         */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        /**
         *发送一个消息
         *1.发送到哪个交换机,""表示不写
         *2.路由的 key 是哪个,扇出类型不用写
         *3.其他的参数信息 MessageProperties.PERSISTENT_TEXT_PLAIN,消息持久化
         *4.发送消息的消息体,消息的二进制
         */
        System.out.print("请输入发送消息内容");
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
            String sendMessage = scanner.next();
            channel.basicPublish(EXCHANGE_NAME,"", null,sendMessage.getBytes("UTF-8"));
            System.out.println("消息发送完毕" + sendMessage);
        }



    }
}

2.消费者1

package com.cloud.mq.test5;

import com.cloud.mq.utils.RabbitMqUtil;
import com.cloud.mq.utils.SleepUtil;
import com.rabbitmq.client.*;

import java.util.Scanner;

/**
 * ReceiveLogs02 把接收到的消息打印在控制台
 */
public class Consumer5a {
    //队列的名字
    private final static String EXCHANGE_NAME = "testExchange";

    //发送消息
    public static void main(String[] args) throws Exception {

        Channel channel = RabbitMqUtil.getChannel();

        /**
         * 声明交换机
         * 1.交换机名字
         * 2.交换机类型
         */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        /**
         * 生成一个临时的队列 队列的名称是随机的
         * 当消费者断开和该队列的连接时 队列自动删除
         */
        String queueName = channel.queueDeclare().getQueue();

        /**
         * binding交换机和队列
         * 1.队列名字
         * 2.交换机名字
         * 3.routing_key,扇出类型,写了也不生效
         */
        channel.queueBind(queueName, EXCHANGE_NAME, "aaaaaaaaaa");

        System.out.println("Consumer5a等待接收消息,把接收到的消息打印在屏幕......");
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            System.out.println("Consumer5a控制台打印接收到的消息" + new String(delivery.getBody()));
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });


    }
}

消费者2

package com.cloud.mq.test5;

import com.cloud.mq.utils.RabbitMqUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.util.Scanner;

/**
 * ReceiveLogs02 将接收到的消息存储在磁盘
 */
public class Consumer5b {
    //队列的名字
    private final static String EXCHANGE_NAME = "testExchange";

    //发送消息
    public static void main(String[] args) throws Exception {

        Channel channel = RabbitMqUtil.getChannel();

        /**
         * 声明交换机
         * 1.交换机名字
         * 2.交换机类型
         */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        /**
         * 生成一个临时的队列 队列的名称是随机的
         * 当消费者断开和该队列的连接时 队列自动删除
         */
        String queueName = channel.queueDeclare().getQueue();

        /**
         * binding交换机和队列
         * 1.队列名字
         * 2.交换机名字
         * 3.routing_key
         */
        channel.queueBind(queueName,EXCHANGE_NAME,"");

        System.out.println("Consumer5b等待接收消息,把接收到的消息打印在屏幕......");
        DeliverCallback deliverCallback = (consumerTag,delivery) -> {
            System.out.println("Consumer5b控制台打印接收到的消息" + new String(delivery.getBody()));
        };

        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
    }
}

4.工具类

package com.cloud.mq.utils;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * 此类为连接工厂创建信道的工具类
 */
public class RabbitMqUtil {

    //得到一个连接的channeer
    public static Channel getChannel() throws Exception {

        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        /**
         *写自己的服务器名字,账号,密码
         */
        factory.setHost("192.168.6.133");
        factory.setUsername("admin");
        factory.setPassword("123");

        //创建连接
        Connection connection = factory.newConnection();
        //获取信道
        Channel channel = connection.createChannel();
        return channel;

    }
}