RabbitMQ篇-各种发送消费模式的使用

一、基本介绍

​ RabbitMQ是对AMQP(即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准的实现)的一种实现,其传输层是使用的TCP协议。

1、virtual host & users

vrital host是虚拟主机,而users用户是需要绑定到对应的虚拟主机的,而发送&接收消息都是面向的对应虚拟主机

在这里插入图片描述

例如RabbitMQ的管理界面

在这里插入图片描述

​ 用户是有对应到virtual host的,同时可以添加用户对应的virtual host

在这里插入图片描述

2、连接模型

在这里插入图片描述

​ 上面是画了生产者,将左边的映射到右边就是消费者。

​ 这里我们可以看到连接到RabbitMQ服务器,然后在具体的消息传输的时候是使用的channel来传输。

二、具体的使用

​ 实现我们可以看下其的官网对其使用的几种模型。首先我们来看下最基本的描叙,及简单的生产消费模型

1、最简单的使用模型

在这里插入图片描述

1)、生产者

public class SimpleProducerMain {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:队列是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        /**
         * 消息发布
         * 参数1:交换机名称(当前没有使用)
         * 参数2:队列名称
         * 参数3:控制消息的特征(例如这条消息需不需要持久化),当前是设置这条消息需要持久化
         * 参数4:消息的内容
         */
        connectionChannel.basicPublish("",queue_1, MessageProperties.PERSISTENT_TEXT_PLAIN,"add msg 1".getBytes());
        System.out.println("msg send success");
        connectionChannel.close();
        connection.close();
    }

}

​ 然后我们运行在去界面查看:

在这里插入图片描述

​ 可以看到目前我们已经有1条准备(Ready)的消息,0条没有ack确认(Unacked)的消息,然后总共是1条消息。

​ 然后我们能看到消息的具体内容

在这里插入图片描述

​ 上面的Messages是能设置获取消息的条数的,例如获取3条消息,当前是获取1条。

2)、消费者

public class SimpleConsumerMain {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:队列是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        connectionChannel.basicConsume(queue_1,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer Get Msg : " + new String(body));
            }
        });
        Thread.sleep(1000L);    //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
        connectionChannel.close();
        connection.close();
    }
}

​ 这个是消费者代码,运行后就会从队列中获取消息打印(这里的basicConsume是使用了多线程)。

Consumer Get Msg : add msg 1
Consumer Get Msg : add msg 1

Process finished with exit code 0

3)、测试

​ 我们试下我们在生产端&消费端不关闭管道&连接看下管理界面的Connections

在这里插入图片描述

​ 可以看到我们目前有两台机连接,然后点击对应的连接就能看到具体的内容

在这里插入图片描述

2、工作队列模型

在这里插入图片描述

​ 这种模型是多个消费者消费同一个队列。

1)、生产者

public class SimpleProducerMain {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        /**
         * 消息发布
         * 参数1:交换机名称(当前没有使用)
         * 参数2:队列名称
         * 参数3:控制消息的特征(例如这条消息需不需要持久化)
         * 参数4:消息的内容
         */
        for (int i = 0; i < 20; i++) {
            connectionChannel.basicPublish("",queue_1, MessageProperties.PERSISTENT_TEXT_PLAIN,("add msg "+i).getBytes());
        }
        System.out.println("msg send success");
//        connectionChannel.close();
//        connection.close();
    }

}

​ 这里其实我们就是改为循环发送消息。

2)、消费者

public class SimpleConsumerMain1 {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        connectionChannel.basicConsume(queue_1,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 2 Get Msg : " + new String(body));
            }
        });
        Thread.sleep(1000L);    //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
//        connectionChannel.close();
//        connection.close();
    }
}
public class SimpleConsumerMain2 {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        connectionChannel.basicConsume(queue_1,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 1 Get Msg : " + new String(body));
            }
        });
        Thread.sleep(1000L);    //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
//        connectionChannel.close();
//        connection.close();
    }

}

​ 这两个消费者的代码都是和前面的是一样的,我们是在打印的时候加了标识Consumer 1&Consumer 2

​ 我们启动两个消费者,然后再启动生产者发送消息

Consumer 1 Get Msg : add msg 1
Consumer 1 Get Msg : add msg 3
Consumer 1 Get Msg : add msg 5
Consumer 1 Get Msg : add msg 7
Consumer 1 Get Msg : add msg 9
Consumer 1 Get Msg : add msg 11
Consumer 1 Get Msg : add msg 13
Consumer 1 Get Msg : add msg 15
Consumer 1 Get Msg : add msg 17
Consumer 1 Get Msg : add msg 19
Consumer 2 Get Msg : add msg 0
Consumer 2 Get Msg : add msg 2
Consumer 2 Get Msg : add msg 4
Consumer 2 Get Msg : add msg 6
Consumer 2 Get Msg : add msg 8
Consumer 2 Get Msg : add msg 10
Consumer 2 Get Msg : add msg 12
Consumer 2 Get Msg : add msg 14
Consumer 2 Get Msg : add msg 16
Consumer 2 Get Msg : add msg 18

​ 我们可以看到两个消费者是1个一个交叉顺序获取消息的

3)、拓展1(ack应答确认)

​ 我们前面的代码是消费者自动ack确认,同时多个消费者平均分配,并且这些消息是一下子全部给了两个消费者,这样可能就会存在问题。例如消费者拿到5个消息后,在处理2个后就宕机了,然后剩下的3个就丢失了,所以我们需要改一下,也就是说我们需要手动在消费者中ACK确认,同时设置一次给一个消息给消费者,这样就避免了上面的问题了。

​ 我们先演示下消息未确认的情况,生产者我们使用相同的代码,消费者我们只启动一个,再将自动确认取消,同时我们也不手动确认:

public class SimpleConsumerMain2 {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        connectionChannel.basicConsume(queue_1,false,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 1 Get Msg : " + new String(body));
            }
        });
        Thread.sleep(1000L);    //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
//        connectionChannel.close();
//        connection.close();
    }
}

在这里插入图片描述

​ 我们启动生产者后可以看到目前我们有了20个准备的消费者,我们再启动消费者:

在这里插入图片描述

​ 可以看到我们没有确认的消息有20个。 这里我们需要注意,我们没有ack应答的消息是可以被消费者再次消费的,例如这里我们可以启动没有修改的SimpleConsumerMain1,其运行的时候就会将这些Unacked消息全都消费。

在这里插入图片描述

4)、拓展2(应答后再给通道另一条消息)

​ 我们修改两个消费者

public class SimpleConsumerMain1 {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        // 设置通道的载入数 设置为1,一次只载入1条
        connectionChannel.basicQos(1);
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        connectionChannel.basicConsume(queue_1,false,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //  参数2:表示只应答确认这条消息
                connectionChannel.basicAck(envelope.getDeliveryTag(),false);
                System.out.println("Consumer 2 Get Msg : " + new String(body) + "- Delivery Tag " + envelope.getDeliveryTag());
            }
        });
        Thread.sleep(1000L);    //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
//        connectionChannel.close();
//        connection.close();
    }

}
public class SimpleConsumerMain2 {

    public  final static String queue_1 = "queue_1";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        connectionChannel.basicQos(1,false);
        /**
         * 通道绑定对应的消息队列
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:该队列是否给这个链接独占
         * 参数4:消费完成后(不再使用),是否自动删除这个队列
         * 参数5:其他参数
         */
        connectionChannel.queueDeclare(queue_1,false,false,
                false,null);
        connectionChannel.basicConsume(queue_1,false,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                connectionChannel.basicAck(envelope.getDeliveryTag(),false);
                System.out.println("Consumer 1 Get Msg : " + new String(body) + "- Delivery Tag " + envelope.getDeliveryTag());
            }
        });
        Thread.sleep(1000L);    //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
//        connectionChannel.close();
//        connection.close();
    }

}

​ 我们再运行:

Consumer 1 Get Msg : add msg 0- Delivery Tag 1
Consumer 1 Get Msg : add msg 3- Delivery Tag 2
Consumer 1 Get Msg : add msg 5- Delivery Tag 3
Consumer 1 Get Msg : add msg 7- Delivery Tag 4
Consumer 1 Get Msg : add msg 9- Delivery Tag 5
Consumer 1 Get Msg : add msg 11- Delivery Tag 6
Consumer 1 Get Msg : add msg 14- Delivery Tag 7
Consumer 1 Get Msg : add msg 17- Delivery Tag 8
Consumer 1 Get Msg : add msg 19- Delivery Tag 9
Consumer 2 Get Msg : add msg 1- Delivery Tag 1
Consumer 2 Get Msg : add msg 2- Delivery Tag 2
Consumer 2 Get Msg : add msg 4- Delivery Tag 3
Consumer 2 Get Msg : add msg 6- Delivery Tag 4
Consumer 2 Get Msg : add msg 8- Delivery Tag 5
Consumer 2 Get Msg : add msg 10- Delivery Tag 6
Consumer 2 Get Msg : add msg 12- Delivery Tag 7
Consumer 2 Get Msg : add msg 13- Delivery Tag 8
Consumer 2 Get Msg : add msg 15- Delivery Tag 9
Consumer 2 Get Msg : add msg 16- Delivery Tag 10
Consumer 2 Get Msg : add msg 18- Delivery Tag 11

​ 可以看到已经不是交叉顺序获取消息了。

​ 我们再修改一下两个消费者,在里面休眠1秒钟(观察还是不是一次就获取大量消息,还是一个一个获取):

 @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //  参数2:表示只应答确认这条消息
                connectionChannel.basicAck(envelope.getDeliveryTag(),false);
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Consumer 2 Get Msg : " + new String(body) + "- Delivery Tag " + envelope.getDeliveryTag());
            }

然后我们看管理界面,能看到消费的曲线了。

在这里插入图片描述

3、广播模型

在这里插入图片描述

​ 广播模型是指我们发送一次消息,然后所有的消费者都能获取到这条消息。其过程是生产者生产消息发送给交换机(exchange),然后交换机面向多个消费者创建多个临时队列传输相同的消息。

1)、生产者

public class ExchangeProducerMain {

    public final static String Exchange_Name = "exchange_20210418";

    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        connectionFactory.setVirtualHost("/virtual_20210417");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");

        Connection connection = connectionFactory.newConnection();
        Channel connectionChannel = connection.createChannel();

        //设置交换机名称、交换机类型
        connectionChannel.exchangeDeclare(Exchange_Name,"fanout");
        for (int i = 0; i < 5; i++) {
            connectionChannel.basicPublish(Exchange_Name,"",null,("exchange msg " + i).getBytes());
        }
        System.out.println("Send Msg success");
        connectionChannel.close();
        connection.close();
    }
}

2)、消费者

public class ExchangeConsumerMain1 {

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        // 设置通道的载入数 设置为1,一次只载入1条
        connectionChannel.basicQos(1);
        // 设置交换机名称、交换机类型
        connectionChannel.exchangeDeclare(Exchange_Name,"fanout");
        //获取交换机临时的队列名称
        String queueName = connectionChannel.queueDeclare().getQueue();
        //  绑定队列、交换机
        connectionChannel.queueBind(queueName,Exchange_Name,"");
        connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(" ExchangeConsumer 1 Get Msg : " + new String(body));
            }
        });
//        connectionChannel.close();
//        connection.close();
    }
}
public class ExchangeConsumerMain2 {

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        ConnectionFactory factory = new ConnectionFactory();
        //创建链接所需的内容
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        //需要注意 用户要配置到这个虚拟主机
        factory.setVirtualHost("/virtual_20210417");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //创建链接
        Connection connection = factory.newConnection();
        //创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
        Channel connectionChannel = connection.createChannel();
        // 设置通道的载入数 设置为1,一次只载入1条
        connectionChannel.basicQos(1);
        // 设置交换机名称、交换机类型
        connectionChannel.exchangeDeclare(Exchange_Name,"fanout");
        //获取交换机临时的队列名称
        String queueName = connectionChannel.queueDeclare().getQueue();
        //  绑定队列、交换机
        connectionChannel.queueBind(queueName,Exchange_Name,"");
        connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(" ExchangeConsumer 2 Get Msg : " + new String(body));
            }
        });
//        connectionChannel.close();
//        connection.close();
    }
}

3)、demo说明

​ 然后我们先启动两个消费者:

在这里插入图片描述

​ 然后可以看到这里就有创建了两个临时队列

在这里插入图片描述

​ 交换机中也有我们新建的交换机了。然后我们运行生产者,看两个消费者的打印信息

 ExchangeConsumer 1 Get Msg : exchange msg 0
 ExchangeConsumer 1 Get Msg : exchange msg 1
 ExchangeConsumer 1 Get Msg : exchange msg 2
 ExchangeConsumer 1 Get Msg : exchange msg 3
 ExchangeConsumer 1 Get Msg : exchange msg 4
 ExchangeConsumer 2 Get Msg : exchange msg 0
 ExchangeConsumer 2 Get Msg : exchange msg 1
 ExchangeConsumer 2 Get Msg : exchange msg 2
 ExchangeConsumer 2 Get Msg : exchange msg 3
 ExchangeConsumer 2 Get Msg : exchange msg 4

​ 可以看到两个消费者都获取了全部的消息。

4、静态路由订阅模型

在这里插入图片描述

​ 我们在前面的广播模型是面向路由的,只要是这个路由的消费者都能获取到消息(我们没有设置routingKey的值),而现在我们这个静态路由订阅是设置,满足路由规则才获取到对应的消息,例如两种独立的级别[info]&[error],我们可以设置只获取[info]的消息,也可以[info]&[error]都获取。

1)、生产者

public class DirectProducerMain {

    public final static String Virtual_Name = "/virtual_20210417";
    public final static String Exchange_Name = "exchange_direct_20210418";
    public final static String User_Name = "guest";
    public final static String Password = "guest";

    public final static String Routing_Error = "error";
    public final static String Routing_Info = "info";

    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        connectionFactory.setVirtualHost(Virtual_Name);
        connectionFactory.setUsername(User_Name);
        connectionFactory.setPassword(Password);

        Connection connection = connectionFactory.newConnection();
        Channel connectionChannel = connection.createChannel();

        connectionChannel.exchangeDeclare(Exchange_Name,"direct");
        //这里是发送 info 消息
        connectionChannel.basicPublish(Exchange_Name,Routing_Info,null," [Info] Msg ".getBytes());
        //这里是发送 error 消息
        connectionChannel.basicPublish(Exchange_Name,Routing_Error,null," [Error] Msg ".getBytes());

        connectionChannel.close();
        connection.close();
    }

}

2)、消费者

public class DirectConsumerMain1 {

    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        connectionFactory.setVirtualHost(Virtual_Name);
        connectionFactory.setUsername(User_Name);
        connectionFactory.setPassword(Password);

        Connection connection = connectionFactory.newConnection();
        Channel connectionChannel = connection.createChannel();

        // 参数:交换机名称、交换机类型
        connectionChannel.exchangeDeclare(Exchange_Name,"direct");
        // 临时队列名称
        String queueName = connectionChannel.queueDeclare().getQueue();
        //  参数:队列名称、路由名称、路由Key  当前是绑定 info
        connectionChannel.queueBind(queueName,Exchange_Name,Routing_Info);

        connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 1 Get Error : " + new String(body));
            }
        });
    }
}
public class DirectConsumerMain2 {


    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        connectionFactory.setVirtualHost(Virtual_Name);
        connectionFactory.setUsername(User_Name);
        connectionFactory.setPassword(Password);

        Connection connection = connectionFactory.newConnection();
        Channel connectionChannel = connection.createChannel();

        // 参数:交换机名称、交换机类型
        connectionChannel.exchangeDeclare(Exchange_Name,"direct");
        // 临时队列名称
        String queueName = connectionChannel.queueDeclare().getQueue();
        //  参数:队列名称、路由名称、路由Key
        //绑定 Info 路由
        connectionChannel.queueBind(queueName,Exchange_Name,Routing_Info);
        //绑定 Error 路由
        connectionChannel.queueBind(queueName,Exchange_Name,Routing_Error);

        connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 2 Get Error&Info : " + new String(body));
            }
        });
    }
}

3)、demo说明

我们先启动两个消费者:

在这里插入图片描述

在这里插入图片描述

​ 我们可以看到目前一个临时队列是只绑定了info、另一个是info&error都绑定了。现在我们运行生产者:

Consumer 1 Get Error :  [Info] Msg 
Consumer 2 Get Error&Info :  [Info] Msg 
Consumer 2 Get Error&Info :  [Error] Msg 

​ 可以看到两个消费者,一个只消费了info,另一个是发送的两个消息都消费了。

5、动态路由订阅模型

​ 前面我们是固定的routingKey,现在我们是模糊匹配

在这里插入图片描述

1)、生产者

public class TopicProducerMain {

    public final static String Virtual_Name = "/virtual_20210417";
    public final static String Exchange_Name = "exchange_topic_20210418";
    public final static String User_Name = "guest";
    public final static String Password = "guest";


    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        connectionFactory.setVirtualHost(Virtual_Name);
        connectionFactory.setUsername(User_Name);
        connectionFactory.setPassword(Password);

        Connection connection = connectionFactory.newConnection();
        Channel connectionChannel = connection.createChannel();

        connectionChannel.exchangeDeclare(Exchange_Name,"topic");

        connectionChannel.basicPublish(Exchange_Name,"save.user",null," [save.user] Msg ".getBytes());
        connectionChannel.basicPublish(Exchange_Name,"save.user.id",null," [save.user.id] Msg ".getBytes());
        connectionChannel.basicPublish(Exchange_Name,"delete.user",null," [delete.user] Msg ".getBytes());
        connectionChannel.basicPublish(Exchange_Name,"delete.user.id",null," [delete.user] Msg ".getBytes());

        connectionChannel.close();
        connection.close();
    }
}

2)、消费者

public class TopicConsumerMain1 {

    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        connectionFactory.setVirtualHost(Virtual_Name);
        connectionFactory.setUsername(User_Name);
        connectionFactory.setPassword(Password);

        Connection connection = connectionFactory.newConnection();
        Channel connectionChannel = connection.createChannel();

        // 参数:交换机名称、交换机类型
        connectionChannel.exchangeDeclare(Exchange_Name,"topic");
        // 临时队列名称
        String queueName = connectionChannel.queueDeclare().getQueue();
        //  参数:队列名称、路由名称、路由Key  这里的一个单词的区分是符号[.],然后[*]表示是任意一个单词
        connectionChannel.queueBind(queueName,Exchange_Name,"save.*");

        connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 1 Get : " + new String(body));
            }
        });
    }
}
public class TopicConsumerMain2 {


    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        connectionFactory.setVirtualHost(Virtual_Name);
        connectionFactory.setUsername(User_Name);
        connectionFactory.setPassword(Password);

        Connection connection = connectionFactory.newConnection();
        Channel connectionChannel = connection.createChannel();

        // 参数:交换机名称、交换机类型
        connectionChannel.exchangeDeclare(Exchange_Name,"topic");
        // 临时队列名称
        String queueName = connectionChannel.queueDeclare().getQueue();
        //  参数:队列名称、路由名称、路由Key  这里的[#]是表示不管后面有多少个单词
        connectionChannel.queueBind(queueName,Exchange_Name,"save.#");
        //绑定 Error 路由
        connectionChannel.queueBind(queueName,Exchange_Name,"delete.*");

        connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 2 Get : " + new String(body));
            }
        });
    }
}

3)、demo说明

​ 我们先启动两个消费者,再启动生产者看下两个消费者的消费内容

Consumer 1 Get :  [save.user] Msg 
Consumer 2 Get :  [save.user] Msg 
Consumer 2 Get :  [save.user.id] Msg 
Consumer 2 Get :  [delete.user] Msg 

​ 这里的关键就是我们前面的注释,即*表示只匹配一个单词、#表示多个单词。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值