5. RabbitMQ之交换机

文章目录

  • 1. 无名交换机
  • 2. Binding绑定
  • 3. 交换机类型
    • 3.1 Fanout类型交换机
    • 3.2 Direct类型交换机
    • 3.3 Topic类型交换机

RabbitMQ的核心思想是生产者生产的消息不会直接发送到队列中,生产者是实际是也不知道发送到哪个队列中的,相反生产者的消息只能发送到交换机中(exchange)。交换机的工作非常简单,它一方面接受来自生产者的消息,另一方面将接收到的消息推送到队列中。交换机清楚知道如何处理收到的消息,是将消息推送到一个指定队列中还是投送到指定的多个队列中,还是直接丢弃消息。关于交换机如何处理消息又是由交换机的类型决定的,不同类型的交换机处理消息的机制不同,交换机类型包括:direct exchange(直接交换机)、topic exchange(主题交换机)、headers exchange(标题交换机)、fanout exchange(扇出交换机)。

交换机其实是一种routingKey,或者叫路由key,生产者生产的消息根据指定的key路由到指定的队列中。

1. 无名交换机

无名交换机其实也是一种direct交换机,在之前几篇文章中发布消息时一直采用下面的形式,第一个参数""就表示无名交换机,也就是如果不送交换机默认采用的交换机。

channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

从RabbitMQ管理端可以看无名交换机如下所示
5. RabbitMQ之交换机_第1张图片
就是说凡是生产者生产的消息没有指定交换机时,都会通过默认的交换机把消息投递到QUEUE_NAME队列中。

2. Binding绑定

Binding其实就是绑定exchange交换机与队列的关系,用来指明交换机绑定了哪个或哪些队列。
如下图所示,生产者向交换机发送消息,交换机绑定了不同的队列,交换机负责根据消息指定的key路由到不同的队列中。
5. RabbitMQ之交换机_第2张图片

下面进入主题,首先看下fanout类型的交换机

3. 交换机类型

3.1 Fanout类型交换机

Fanout交换机也叫扇出交换机,生产者发送消息到Fanout交换机后,Fanout交换机负责把消息路由到交换机绑定的所有队列中,每个队列对应的消费者再分别进行消费消息。相当于生产者发布一个消息,所有消费者都消费一遍,类似于广播功能,一个主讲人进行广播,所有听众进行接收消息。

案例:一个消费者生产消息,生产的消息发向MY_EXCHANGE_NAME交换机中, MY_EXCHANGE_NAME交换机通过routingKey为hello字符串绑定了MY_QUEUE1和MY_QUEUE2两个队列,MY_QUEUE1队列中消息由Consumer1消费者负责消费,MY_QUEUE2队列中消息负责由Consumer2和Consumer3消费者消费者轮询消费。

代码实现如下所示:

生产者Producer代码, 其中涉及到的RabbitmqUtil类参见此篇文章

public class Producer {
    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*创建信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /* 声明一个交换机
        * EXCHANGE_NAME为交换机的名字
        * FANOUT为交换机的类型
        * */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        String message = null;
        for(int i=0; i<10; i++){
            message = "hello world " + i;
            /*
             * 发送消息
             * 1. 发送到哪个交换机
             * 2. 指定路由的key是哪个
             * 3. 其它参数信息
             * 4. 消息体
             * */
            channel.basicPublish(EXCHANGE_NAME, "hello", null, message.getBytes("UTF-8"));
            System.out.println("hello world " + i + "消息发送完毕");
        }
    }
}

消费者Consumer1代码

public class Consumer1 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME";
    private static final String QUEUE_NAME = "MY_QUEUE1";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*声明一个队列*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "hello");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer1处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer1消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

消费者Consumer2代码

public class Consumer2 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME";
    private static final String QUEUE_NAME = "MY_QUEUE2";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*声明一个队列*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "hello");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer2处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer2消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

消费者Consumer3代码

public class Consumer3 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME";
    private static final String QUEUE_NAME = "MY_QUEUE2";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "hello");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer3处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer3消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

下面进行测试,分别运行Procuder和三个消费者代码,执行结果如下
Producer输出如下,表示生产了10个hello world消息发向了交换机中

hello world 0消息发送完毕
hello world 1消息发送完毕
hello world 2消息发送完毕
hello world 3消息发送完毕
hello world 4消息发送完毕
hello world 5消息发送完毕
hello world 6消息发送完毕
hello world 7消息发送完毕
hello world 8消息发送完毕
hello world 9消息发送完毕

Consumer1输出如下所示,表示Consumer1正好消费了MY_QUEUE1队列中10个hello world消息

消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 0
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 1
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 2
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 3
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 4
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 5
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 6
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 7
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 8
消费者Consumer1处理完毕 MY_QUEUE1 队列中的消息 hello world 9

Consumer2输出如下所示,表示Consumer2正好消费了MY_QUEUE2队列中5个hello world消息

消费者Consumer2处理完毕 MY_QUEUE2 队列中的消息 hello world 0
消费者Consumer2处理完毕 MY_QUEUE2 队列中的消息 hello world 2
消费者Consumer2处理完毕 MY_QUEUE2 队列中的消息 hello world 4
消费者Consumer2处理完毕 MY_QUEUE2 队列中的消息 hello world 6
消费者Consumer2处理完毕 MY_QUEUE2 队列中的消息 hello world 8

Consumer3输出如下所示,表示Consumer3正好也消费了MY_QUEUE2队列中5个hello world消息

消费者Consumer3处理完毕 MY_QUEUE2 队列中的消息 hello world 1
消费者Consumer3处理完毕 MY_QUEUE2 队列中的消息 hello world 3
消费者Consumer3处理完毕 MY_QUEUE2 队列中的消息 hello world 5
消费者Consumer3处理完毕 MY_QUEUE2 队列中的消息 hello world 7
消费者Consumer3处理完毕 MY_QUEUE2 队列中的消息 hello world 9

说明生产者生产的hello world0~9的消息发向了MY_EXCHANGE_NAME交换机,并且指定的routingKey为hello,那么MY_EXCHANGE_NAME交换机就会把消息通过路由到通过routingKey绑定的队列中,此案例中MY_EXCHANGE_NAME通过routingKey分别绑定到了MY_QUEUE1队列和MY_QUEUE2队列,MY_QUEUE1队列中消息全部由Consumer1消费,MY_QUEUE2队列中消息由Consumer2和Consumer3轮询进行消费,轮询消费详解参考此篇文章。

3.2 Direct类型交换机

Fanout类型交换机类似于广播形式,把消息发到所有绑定的队列中,而Direct交换机绑定了不同的routingKey,通过不同的routingKey绑定了不同的队列。如下图所示,MY_EXCHANGE_NAME1交换机通过hello_queue3字符串绑定了queue3队列,通过hello_queue4绑定了queue4队列,生产者向交换机发布消息时,不同种类的消息就会被路由到不同的队列中,然后Consumer3消费queue3中的消息,Consumer4消费queue4。
5. RabbitMQ之交换机_第3张图片
下面通过代码实现上图所示,生产者发布消息到MY_EXCHANGE_NAME1交换机,交换机通过hello_queue3绑定到queue3队列,通过hello_queue4绑定到queue4队列,生产者生产的消息偶数会被路由到queue3队列中,奇数消息会被路由奥queue4队列中。

生产者Producer代码如下

public class Producer {
    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME1";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*创建信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /* 声明一个交换机
        * EXCHANGE_NAME为交换机的名字
        * FANOUT为交换机的类型
        * */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        String message = null;
        for(int i=0; i<10; i++){
            message = "hello world " + i;
            if (i%2 == 0){
                /*
                 * 发送消息
                 * 1. 发送到哪个交换机
                 * 2. 指定路由的key是哪个
                 * 3. 其它参数信息
                 * 4. 消息体
                 * */
                channel.basicPublish(EXCHANGE_NAME, "hello_queue3", null, message.getBytes("UTF-8"));
            }else {
                channel.basicPublish(EXCHANGE_NAME, "hello_queue4", null, message.getBytes("UTF-8"));
            }
            System.out.println("hello world " + i + "消息发送完毕");
        }
    }
}

消费者Consumer3

public class Consumer3 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME1";
    private static final String QUEUE_NAME = "MY_QUEUE3";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*声明一个队列*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "hello_queue3");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer3处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer3消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

消费者Consumer4

public class Consumer4 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME1";
    private static final String QUEUE_NAME = "MY_QUEUE4";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*声明一个队列*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "hello_queue4");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer4处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer4消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

分别运行Consumer1、Consumer2和Producer代码

Producer输出如下所示,表示生产了hello world 0~9消息

hello world 0消息发送完毕
hello world 1消息发送完毕
hello world 2消息发送完毕
hello world 3消息发送完毕
hello world 4消息发送完毕
hello world 5消息发送完毕
hello world 6消息发送完毕
hello world 7消息发送完毕
hello world 8消息发送完毕
hello world 9消息发送完毕

Consumer3输出如下所示,全部为偶数消息,表示Consumer3消费了MY_QUEUE3队列中的消息

消费者Consumer3处理完毕 MY_QUEUE1 队列中的消息 hello world 0
消费者Consumer3处理完毕 MY_QUEUE1 队列中的消息 hello world 2
消费者Consumer3处理完毕 MY_QUEUE1 队列中的消息 hello world 4
消费者Consumer3处理完毕 MY_QUEUE1 队列中的消息 hello world 6
消费者Consumer3处理完毕 MY_QUEUE1 队列中的消息 hello world 8

Consumer3输出如下所示,全部为奇数消息,表示Consumer4消费了MY_QUEUE4队列中消息。

消费者Consumer4处理完毕 MY_QUEUE1 队列中的消息 hello world 1
消费者Consumer4处理完毕 MY_QUEUE1 队列中的消息 hello world 3
消费者Consumer4处理完毕 MY_QUEUE1 队列中的消息 hello world 5
消费者Consumer4处理完毕 MY_QUEUE1 队列中的消息 hello world 7
消费者Consumer4处理完毕 MY_QUEUE1 队列中的消息 hello world 9

由此可见,DIRECT类型的交换机,适合把不同类型的消息通过绑定的key路由到不同的队列中。如果把DIRECT类型交换机routingKey都设为一致,则direct类型的交换机就如同FANOUT类型交换机了。

3.3 Topic类型交换机

topic类型交换机功能更强大,它可以实现Fanout类型交换机和Direct类型交换机所有功能。topic类型交换机功能之所以强大,是因为topic交换机的routingKey支持类似于正则表达式的方式来绑定队列。比如队列A和topic交换机设置的绑定routingKey为 big.red.*,其中点号表示可以匹配任何字符串,那么凡是生产者向topic交换机发布消息时指定的routingKey符合big.red.*规则的都会被路由到队列A中,例如生产者向交换机发布消息时指定的routingKey为big.red.apple,那么这个消息会被路由到队列A中,如果生产者指定的routingKey为big.red.tomato,那么这个消息也会被路由到队列中,但如果为little.red.apple消息就不会路由到队列A中,因为little.red.apple不符合 big.red.*规则。

匹配规则:

  1. 其中 * 号表示可以匹配一个字符串;
  2. 其中#可以匹配0个或多个字符串;

例如:
big.blue.apple 匹配big..apple规则 ,同时也匹配 #.apple规则;
big.red.apple 匹配big.
.apple规则,同时也匹配 .red.apple规则,也匹配 #.apple规则;
big.gold.apple 匹配big.
.apple规则 ,同时也匹配 #.apple规则;
little.red.apple 匹配*.red.apple规则,同时也匹配 #.apple规则;
little.blue.apple 匹配 #.apple规则;

5. RabbitMQ之交换机_第4张图片
案例:下面通过代码实现上述流程图,一个TOPIC类型的MY_EXCHANGE_NAME2交换机,该交换机通过 big.*.apple 绑定MY_QUEUE5队列,通过 *.red.apple 绑定MY_QUEUE6队列,通过 #.apple 绑定MY_QUEUE7队列。

首先创建生产者类Producer类

public class Producer {
    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME2";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*创建信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /* 声明一个交换机
        * EXCHANGE_NAME为交换机的名字
        * FANOUT为交换机的类型
        * */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        /*
         * 发送消息
         * 1. 发送到哪个交换机
         * 2. 指定路由的key是哪个
         * 3. 其它参数信息
         * 4. 消息体
         * */
        channel.basicPublish(EXCHANGE_NAME, "big.blue.apple", null, "又大又青的苹果".getBytes("UTF-8"));
        System.out.println("又大又青的苹果");
        channel.basicPublish(EXCHANGE_NAME, "big.red.apple", null, "又大又红的苹果".getBytes("UTF-8"));
        System.out.println("又大又红的苹果");
        channel.basicPublish(EXCHANGE_NAME, "big.gold.apple", null, "又大又金色的苹果".getBytes("UTF-8"));
        System.out.println("又大又金色的苹果");
        channel.basicPublish(EXCHANGE_NAME, "little.red.apple", null, "即小又红色的苹果".getBytes("UTF-8"));
        System.out.println("既小又红色的苹果");
        channel.basicPublish(EXCHANGE_NAME, "little.blue.apple", null, "既小又青色的苹果".getBytes("UTF-8"));
        System.out.println("既小又青色的苹果");
    }
}

创建消费者类Consumer5

public class Consumer5 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME2";
    private static final String QUEUE_NAME = "MY_QUEUE5";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*声明一个队列*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "big.*.apple");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer5处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer5消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

创建消费者类Consumer6

public class Consumer6 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME2";
    private static final String QUEUE_NAME = "MY_QUEUE6";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*声明一个队列*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "*.red.apple");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer6处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer6消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

创建消费者类Consumer7

public class Consumer7 {

    private static final String EXCHANGE_NAME = "MY_EXCHANGE_NAME2";
    private static final String QUEUE_NAME = "MY_QUEUE7";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*获取信道*/
        Channel channel = RabbitmqUtil.getChannel();
        /*声明一个队列*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*绑定交换机和队列, 绑定的routingKey为空字符串*/
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "#.apple");
        /*消费消息时回调的接口*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("消费者Consumer7处理完毕 " + QUEUE_NAME + " 队列中的消息 " + new String(message.getBody()));
        };
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消费者Consumer7消费消息失败!");
        };
        /*
         * 消费者消费消息
         * 1. 第一个参数代表消费哪个队列
         * 2. 第二个参数表示消息被消费成功后是否自动向服务器发送应答。true表示自动向服务器发送应答, false表示需要手动向服务器发送应答
         * */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

下面进行测试,首先分别运行3个消费者类,注意一定要提前创建好MY_EXCHANGE_NAME2交换机,消费者类都启动后,再运行生产者。
生产者终端输出如下所示,表示生产者消息发送完毕,总共发送了5条消息,如下所示

又大又青的苹果
又大又红的苹果
又大又金色的苹果
既小又红色的苹果
既小又青色的苹果

Consumer5终端输出如下所示,表示Consumer5消费者消费的消息都是 big.*.apple类型的消息。

消费者Consumer5处理完毕 MY_QUEUE5 队列中的消息 又大又青的苹果
消费者Consumer5处理完毕 MY_QUEUE5 队列中的消息 又大又红的苹果
消费者Consumer5处理完毕 MY_QUEUE5 队列中的消息 又大又金色的苹果

Consumer6终端输出如下所示,表示Consumer6消费者消费的消息都是 *.red.apple类型的消息。

消费者Consumer6处理完毕 MY_QUEUE6 队列中的消息 又大又红的苹果
消费者Consumer6处理完毕 MY_QUEUE6 队列中的消息 即小又红色的苹果

Consumer7终端输出如下所示,表示Consumer7消费者消费了生产者生产的所有消息,因为所有消息都匹配 #.apple规则。

消费者Consumer7处理完毕 MY_QUEUE7 队列中的消息 又大又青的苹果
消费者Consumer7处理完毕 MY_QUEUE7 队列中的消息 又大又红的苹果
消费者Consumer7处理完毕 MY_QUEUE7 队列中的消息 又大又金色的苹果
消费者Consumer7处理完毕 MY_QUEUE7 队列中的消息 即小又红色的苹果
消费者Consumer7处理完毕 MY_QUEUE7 队列中的消息 既小又青色的苹果

FANOUT类型交换机、DIRECT类型交换机和TOPIC类型交换机之间关系
如果当队列绑定routingKey为 #,那么所有队列将接受所有消息,就类似于FANOUT型交换机了;
如果当队列绑定routingKey中没有 #* 出现,就类似于DIRECT 交换机了。

你可能感兴趣的:(rabbitmq,rabbitmq,交换机,FANOUT,DIRECT,TOPIC)