RabbitMQ五种工作模式

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/BruceLiu_code/article/details/102761636

1.简介

最近,在看一些消息中间件的内容,之前都没有好好学习一下消息中间件。本文将对RabbitMQ中五种常用的工作模式做一个简单的介绍和总结。RabbitMQ常用的工作模式有:简单队列模式、工作队列模式、发布订阅模式、路由模式、主题模式。本文参照RabbitMQ官网示例总结,详细可以到官网查看:https://www.rabbitmq.com/getstarted.html

2.简单队列模式(Simple Queue)

【模型图】
只包含一个生产者以及一个消费者,生产者Producer将消息发送到队列中,消费者Consumer从该队列接收消息。(单生产单消费)
在这里插入图片描述
上图中,“P”是我们的生产者,“C”是我们的消费者。

【获取MQ连接对象工具类】

package com.bruceliu.utils;

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


/**
 * @author bruceliu
 * @create 2019-10-26 21:48
 * @description 获取RabbitMQ的连接工具类
 */
public class MQConnecitonUtils {

    private static final String RABBITMQ_HOST = "127.0.0.1";
    private static final Integer RABBITMQ_PORT = 5672;
    private static final String RABBITMQ_VHOST = "/";
    private static final String RABBITMQ_USERNAME = "guest";
    private static final String RABBITMQ_PASSWORD = "guest";

    public static Connection getConnection() {
        //定义MQ连接对象
        Connection connection = null;
        //创建MQ连接工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置MQ主机名称
        connectionFactory.setHost(RABBITMQ_HOST);
        // 设置MQ AMQP端口号
        connectionFactory.setPort(RABBITMQ_PORT);
        // 设置MQ 连接的virtual host
        connectionFactory.setVirtualHost(RABBITMQ_VHOST);
        // 设置MQ 用户名称
        connectionFactory.setUsername(RABBITMQ_USERNAME);
        // 设置MQ 用户密码
        connectionFactory.setPassword(RABBITMQ_PASSWORD);
        try {
            connection = connectionFactory.newConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回连接对象
        return connection;
    }
}

【生产者】

package com.bruceliu.producer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.Date;

/**
 * @author bruceliu
 * @create 2019-10-26 21:50
 * @description
 */
public class ProducerTest01 {

    private static final String SIMPLE_QUEUE_NAME = "MQ_SIMPLE_QUEUE";

    private static final String SIMPLE_QUEUE_MESSAGE = "Hello World!"+new Date();

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            //创建通道
            channel = connection.createChannel();
            //创建Queue队列
            channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
            //发送消息到队列MQ_SIMPLE_QUEUE
            //basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
            channel.basicPublish("", SIMPLE_QUEUE_NAME, null, SIMPLE_QUEUE_MESSAGE.getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【消费者】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 21:52
 * @description
 */
public class ConsumerTest001 {

    private static final String SIMPLE_QUEUE_NAME = "MQ_SIMPLE_QUEUE";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        Channel channel;
        try {
            //创建消息通道对象
            channel = connection.createChannel();
            //声明queue队列
            channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("receive message: " + message);
                }
            };
            //监听消息队列
            channel.basicConsume(SIMPLE_QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【运行结果】
在这里插入图片描述

3.工作队列模式(Work Queues)

【模型图
多个消费者绑定到同一个队列上,一条消息只能被一个消费者进行消费。工作队列有轮训分发和公平分发两种模式。
在这里插入图片描述
下面先说说轮训分发(round-robin)方式:

【消息生产者】

package com.bruceliu.producer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.Date;

/**
 * @author bruceliu
 * @create 2019-10-26 22:02
 * @description
 * 说明:
 * 消费者1与消费者2处理的消息是均分的,而且消息是轮训分发的(轮训分发 round-robin)
 */
public class ProducerTest02 {

    private static final String WORK_QUEUE_NAME = "MQ_WORK_QUEUE";
    private static final String WORK_QUEUE_MESSAGE = "hello world!! ------> "+new Date();

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建Queue队列
            channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);
            //发送10条消息到工作队列
            for (int i = 1; i <= 10; i++) {
                StringBuilder msg = new StringBuilder(WORK_QUEUE_MESSAGE).append(i);
                //发送消息
                channel.basicPublish("", WORK_QUEUE_NAME, null, msg.toString().getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【消息消费者A】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 22:06
 * @description
 */
public class ConsumerTest02_A {

    private static final String WORK_QUEUE_NAME = "MQ_WORK_QUEUE";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        Channel channel = null;
        try {
            //创建消息通道对象
            channel = connection.createChannel();
            //声明queue队列
            channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);
            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer-A】receive message: " + message);
                    try {
                        //模拟延迟
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };

            //监听消息队列
            channel.basicConsume(WORK_QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【消息消费者B】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author bruceliu
 * @create 2019-10-26 22:07
 * @description
 */
public class ConsumerTest02_B {

    private static final String WORK_QUEUE_NAME = "MQ_WORK_QUEUE";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        Channel channel = null;
        try {
            //创建消息通道对象
            channel = connection.createChannel();
            //声明queue队列
            channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);
            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer-B】receive message: " + message);
                    try {
                        //模拟延迟
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            //监听消息队列
            channel.basicConsume(WORK_QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【运行结果】
在这里插入图片描述在这里插入图片描述在这里插入图片描述由上面图可见,消费者1和消费者2处理的消息是均分的(消费的消息条数一样),而且消息是轮询分发的,也就是说同一个消息只能被一个消费者消费。上面的消费者1和消费者2处理消息的效率不同,但是最后接收到的消息还是一样多,如果需要让工作效率高的消费者消费更多的消息,那么可以使用公平分发,下面介绍一下工作队列的公平分发模式(能者多劳)。

【生产者】

package com.bruceliu.producer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 22:42
 * @description  工作队列 - 消息生产者 (公平分发方式Fair dispatch)
 * 说明:
 * 1. 生产者、消费者指定:channel.basicQos(1);
 * 2. 消费者消费完消息自动发送确认消息:channel.basicAck(envelope.getDeliveryTag(), false);
 * 3. 消费者必须关闭自动应答:autoAck = false;
 * 4. 一般消费者如果处理消息的时间较短(效率较高),那么它处理的消息会比较多一些;
 */
public class ProducerTest03 {

    private static final String WORK_QUEUE_NAME = "MQ_WORK_QUEUE";
    private static final String WORK_QUEUE_MESSAGE = "hello world!! ------> ";

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建Queue队列
            channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);

            //每个消费者发送确认消息之前,消息队列不发送下一个消息到消费者(同一时刻服务器只会发送一条消息给消费者),消费者端发送了ack后才会接收下一个消息。
            channel.basicQos(1);

            //发送10条消息到工作队列
            for (int i = 1; i <= 10; i++) {
                StringBuilder msg = new StringBuilder(WORK_QUEUE_MESSAGE).append(i);
                //发送消息
                channel.basicPublish("", WORK_QUEUE_NAME, null, msg.toString().getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

【消费者A】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 22:43
 * @description
 */
public class ConsumerTest03_A {

    private static final String WORK_QUEUE_NAME = "MQ_WORK_QUEUE";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //声明queue队列
            channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer-A】receive message: " + message);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答(autoAck:true自动返回结果,false手动返回)
            boolean autoAck = false;

            //监听消息队列
            channel.basicConsume(WORK_QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【消费者B】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 22:45
 * @description
 */
public class ConsumerTest03_B {

    private static final String WORK_QUEUE_NAME = "MQ_WORK_QUEUE";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //声明queue队列
            channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);

            channel.basicQos(1);
            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body,"UTF-8");
                    System.out.println(("【CustomConsumer-B】receive message: " + message));
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答
            boolean autoAck = false;
            //监听消息队列
            channel.basicConsume(WORK_QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【运行结果】
在这里插入图片描述
在这里插入图片描述
由此可见,消费者2的效率相对较高,所以消费者2消费消息比消费者1多一些,这样就可以充分发挥消费者处理消息的能力。

注意点:
1. 生产者、消费者指定:channel.basicQos(1);
2. 消费者消费完消息自动发送确认消息:channel.basicAck(envelope.getDeliveryTag(), false);
3. 消费者必须关闭自动应答:autoAck = false;
4. 一般消费者如果处理消息的时间较短(效率较高),那么它处理的消息会比较多一些;

4.发布-订阅模式(Publish/Subscribe)

【模型图】
生产者将消息发送到交换器,然后交换器绑定到多个队列,监听该队列的所有消费者消费消息。
在这里插入图片描述
【生产者】

package com.bruceliu.producer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 22:58
 * @description
 * 说明:可实现一条消息被多个消费者消费
 * a. 一个生产者,多个消费者;
 * b. 每一个消费者都有自己的消息队列;
 * c. 生产者没有把消息发送到队列,而是发送到交换器exchange上;
 * d. 每个队列都需要绑定到交换机上;
 * e. 生产者生产的消息先经过交换机然后到达队列,一个消息可以被多个消费者消费;
 */
public class ProducerTest04 {

    private static final String PUBLISH_SUBSCRIBE_EXCHANGE_NAME = "publish_subscribe_exchange_fanout";
    //类型:分发
    private static final String PUBLISH_SUBSCRIBE_EXCHANGE_TYPE = "fanout";

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建交换机对象publish_subscribe_exchange_fanout
            channel.exchangeDeclare(PUBLISH_SUBSCRIBE_EXCHANGE_NAME, PUBLISH_SUBSCRIBE_EXCHANGE_TYPE);
            //发送消息到交换机exchange上
            String msg = "hello world!!!";
            channel.basicPublish(PUBLISH_SUBSCRIBE_EXCHANGE_NAME, "", null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【消费者A】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 22:59
 * @description
 */
public class ConsumerTest04_A {

    private static final String PUBLIC_SUBSCRIBE_QUEUE_NAME = "public_subscribe_queue_name01";

    private static final String PUBLISH_SUBSCRIBE_EXCHANGE_NAME = "publish_subscribe_exchange_fanout";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(PUBLIC_SUBSCRIBE_QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上
            channel.queueBind(PUBLIC_SUBSCRIBE_QUEUE_NAME, PUBLISH_SUBSCRIBE_EXCHANGE_NAME, "");

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer01】receive message: " + message);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答
            boolean autoAck = false;
            //监听消息队列
            channel.basicConsume(PUBLIC_SUBSCRIBE_QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【消费者B】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author bruceliu
 * @create 2019-10-26 23:03
 * @description
 */
public class ConsumerTest04_B {

    private static final String PUBLIC_SUBSCRIBE_QUEUE_NAME = "public_subscribe_queue_name02";
    private static final String PUBLISH_SUBSCRIBE_EXCHANGE_NAME = "publish_subscribe_exchange_fanout";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(PUBLIC_SUBSCRIBE_QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上
            channel.queueBind(PUBLIC_SUBSCRIBE_QUEUE_NAME, PUBLISH_SUBSCRIBE_EXCHANGE_NAME, "");

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer02】receive message: " + message);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答
            boolean autoAck = false;
            //监听消息队列
            channel.basicConsume(PUBLIC_SUBSCRIBE_QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【运行结果】
在这里插入图片描述
在这里插入图片描述
由此可见,一条消息同时被两个消费者同时消费。

【交换机绑定信息】
在这里插入图片描述注意点:

a. 一个生产者,多个消费者;
b. 每一个消费者都有自己的消息队列,分别绑定到不同的队列上;
c. 生产者没有把消息发送到队列,而是发送到交换器exchange上;
d. 每个队列都需要绑定到交换机上;
e. 生产者生产的消息先经过交换机然后到达队列,一个消息可以被多个消费者消费;
f. 如果消息发送到没有队列绑定的交换器时,消息将会丢失,因为交换器没有存储消息的能力,只有队列才有存储消息的能力;

5.路由模式(Routing)

【模型图】
生产者将消息发送到direct交换器,它会把消息路由到那些binding key与routing key完全匹配的Queue中,这样就能实现消费者有选择性地去消费消息。

【生产者】

package com.bruceliu.producer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 23:16
 * @description 说明:生产者发送消息的时候指定routing key,然后消费者绑定队列的时候也指定一些binding key,只有binding key与routing key一致的消费者才能接收到此消息
 */
public class ProducerTest05 {

    private static final String EXCHANGE_NAME = "publish_subscribe_exchange_direct";
    //交换机类型:direct
    private static final String EXCHANGE_TYPE = "direct";

    private static final String EXCHANGE_ROUTE_KEY = "info";

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建交换机对象
            channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
            //发送消息到交换机exchange上
            String msg = "hello world!!!!";
            //指定routing key为info
            channel.basicPublish(EXCHANGE_NAME, EXCHANGE_ROUTE_KEY, null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【消费者A】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 23:22
 * @description
 */
public class Consumer05_A {

    private static final String QUEUE_NAME = "routing_direct_queue_name";

    private static final String EXCHANGE_NAME = "publish_subscribe_exchange_direct";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY = "error";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY);

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer01】receive message: " + message);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答
            boolean autoAck = false;
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【消费者B】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 23:24
 * @description
 */
public class Consumer05_B {

    private static final String QUEUE_NAME = "routing_direct_queue_name02";
    private static final String EXCHANGE_NAME = "publish_subscribe_exchange_direct";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY01 = "error";
    private static final String EXCHANGE_ROUTE_KEY02 = "info";
    private static final String EXCHANGE_ROUTE_KEY03 = "warning";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY01);
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY02);
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY03);

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer02】receive message: " + message);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答
            boolean autoAck = false;
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【运行结果】
在这里插入图片描述
在这里插入图片描述
因为生产者发布消息的时候指定了routing key为info, 消费者绑定队列的时候指定的binding key 为error,显然消费者1接收不到此消息,因为消费者2绑定队列的时候指定了binding key为error、info、warning,所以消费者2能够成功接收该消息进行消费。

【交换机绑定信息】
在这里插入图片描述

6.主题(Topic)模式

【模型图】
类似于正则表达式匹配的一种模式。主要使用#、*进行匹配。
在这里插入图片描述
【生产者】

package com.bruceliu.producer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 23:30
 * @description
 * 说明:
 * #: 代表一个或者多个
 * *: 代表一个
 *
 * 举例:
 * 比如发送消息的时候指定了routing key为news.insert,
 * 如果消费者指定binding key 为news.* 或者news.#都能接收到该消息;
 *
 */
public class ProducerTest06 {
    private static final String EXCHANGE_NAME = "exchange_topic";
    //交换机类型:topic 类似正则匹配模式
    private static final String EXCHANGE_TYPE = "topic";
    //指定routing key
    private static final String EXCHANGE_ROUTE_KEY = "news.insert";

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建交换机对象
            channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
            //发送消息到交换机exchange上
            String msg = "hello world!!!";
            channel.basicPublish(EXCHANGE_NAME, EXCHANGE_ROUTE_KEY, null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【消费者A】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 23:31
 * @description
 */
public class Consumer06_A {

    private static final String QUEUE_NAME = "topic_queue_name1";
    private static final String EXCHANGE_NAME = "exchange_topic";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY = "news.insert";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY);

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer01】receive message: " + message);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答
            boolean autoAck = false;
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【消费者B】

package com.bruceliu.consumer003;

import com.bruceliu.utils.MQConnecitonUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author bruceliu
 * @create 2019-10-26 23:32
 * @description
 */
public class Consumer06_B {

    private static final String QUEUE_NAME = "topic_queue_name2";
    private static final String EXCHANGE_NAME = "exchange_topic";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY = "news.#";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY);

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, "UTF-8");
                    System.out.println("【CustomConsumer02】receive message: " + message);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //消费完一条消息需要自动发送确认消息给MQ
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };

            //使用公平分发必须关闭自动应答
            boolean autoAck = false;
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【运行结果】
在这里插入图片描述
在这里插入图片描述
生产者发送消息绑定的routing key 为news.insert;消费者1监听的队列和交换器binding key 为news.insert;消费者2监听的队列和交换器bindingkey为news.#,很显然,两个消费者都将接收到该消息。

【交换机绑定信息】
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/BruceLiu_code/article/details/102761636