RabbitMQ几种工作模式

RabbitMQ入门中我们提到RabbitMQ几种不同的通讯方式,

pom

        <!-- com.rabbitmq/amqp-client -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.9.0</version>
        </dependency>
        <!-- 测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

RabbitMQClient

public class RabbitMQClient {

    public static Connection getConnection(){
        ConnectionFactory factory = new ConnectionFactory();
        //指定IP和端口号
        factory.setHost("192.168.0.109");
        factory.setPort(5672);
        //用户名和密码
        factory.setUsername("test");
        factory.setPassword("test");
        //指定vHost
        factory.setVirtualHost("/test");
        Connection connection = null;
        try {
            connection = factory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return connection;
    }
}

在操作之前先提一下 ACK的消息确认机制是消费者从RabbitMQ收到消息并处理完成后,反馈给RabbitMQ,RabbitMQ收到反馈后才将此消息从队列中删除。

1、"Hello World!"

它是一个生产者、一个消费者

Publisher
public class Publisher {

    @Test
    public void publish() throws IOException, TimeoutException {
        Connection connection = RabbitMQClient.getConnection();

        //创建Channel
        Channel channel = connection.createChannel();

        String msg = "Hello-World";
        //发布消息到exchange,同时指定路由规则
        /**
         * 参数1 : 指定exchange  ""表示使用默认的exchange
         * 参数2 : 指定路由规则,使用具体的队列名称
         * 参数3 : 指定传递的消息所携带的properties
         * 参数4 : 指定发布的具体消息 byte[]类型
         * PS : exchange不会把消息持久化到本地,Queue才会持久化消息
         */
        channel.basicPublish("","HelloWorld",null,msg.getBytes());
        System.out.println("生产者发布消息成功!");
        //释放资源
        channel.close();
        connection.close();
    }
}
Consumer
public class Consumer {

    @Test
    public void consume() throws IOException, TimeoutException {
        Connection connection = RabbitMQClient.getConnection();
        Channel channel = connection.createChannel();

        //声明队列  HelloWorld
        /**
         * 参数1 : 指定队列名称
         * 参数2 : 当前队列是否需要持久化
         * 参数3 : 是否排外(connection.close()-当前队列会被自动删除,当前队列只能被一个消费者消费)
         * 参数4 : 如果队列没有消费者在消费,则自动删除
         * 参数5 : 指定当前队列的其他信息
         */
        channel.queueDeclare("HelloWorld",true,false,false,null);
        //开启监听Queue
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到消息:" + new String(body,"UTF-8"));
            }
        };
        /**
         * 参数1:指定消费哪个队列
         * 参数2:指定是否自动ACK(true,接收到消息后,会立即告诉RabbitMQ)
         * 参数3:指定消费回调
         */
        channel.basicConsume("HelloWorld",true,defaultConsumer);
        System.in.read();
        //释放资源
        channel.close();
        connection.close();

    }
}

分别启动消费者、生产者

2、Work Queues

一个生产者有两个消费者

 Publisher

public class Publisher {

    @Test
    public void publish() throws IOException, TimeoutException {
        //获取Connection
        Connection connection = RabbitMQClient.getConnection();
        //创建Channel
        Channel channel = connection.createChannel();
        //发布消息到exchange,同时指定路由的规则
        for (int i = 0; i < 10; i++) {
            String msg = "Work Queues" + i;
            channel.basicPublish("","WorkQueues",null,msg.getBytes());
        }
        System.out.println("生产者发布消息成功!");
        //释放资源
        channel.close();
        connection.close();
    }
}

对于两个消费者的代码和上面的一样,只需要把消费队列的名称改为 WorkQueues

依次启动消费者、生产者

根据结果可以看出,两位消费者平均消费了。 但是考虑到实际情况中消费能力不一样,调整代码再次测试。

将自动ACK改为手动ACK,并且模拟消费能力不一致,在每次消费时让消费者休眠不同时间,Consumer2中的代码一致,只需要修改休眠时间为200

public class Consumer1 {

    @Test
    public void consume() throws IOException, TimeoutException {
        Connection connection = RabbitMQClient.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("WorkQueues",true,false,false,null);

        //指定当前消费者,一次消费多少条消息
        channel.basicQos(1);
        //开启监听Queue
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //为了体现消费能力不同,让两个消费者消费一次休眠不同的时间,这里2号休眠时间是1号的2倍
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("1号消费者接收到消息:" + new String(body,"UTF-8"));
                //手动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        /**
         * 参数1:指定消费哪个队列
         * 参数2:指定是否自动ACK(true,接收到消息后,会立即告诉RabbitMQ)
         * 参数3:指定消费回调
         */
        channel.basicConsume("WorkQueues",false,defaultConsumer);
        System.in.read();
        //释放资源
        channel.close();
        connection.close();

    }
}

依次启动消费者、生产者

3、Publish / Subscribe

一个消息发送给多个消费者,这种模式一般被称为“发布/订阅”模式。

很明显这里需要自己创建一个Exchange             

Publisher

public class Publisher {
    @Test
    public void publish() throws IOException, TimeoutException {
        Connection connection = RabbitMQClient.getConnection();
        //创建Channel
        Channel channel = connection.createChannel();
        //创建Exchange——绑定某一个队列
        /**
         * 参数1:exchange的名称
         * 参数2:指定exchange类型
         */
        channel.exchangeDeclare("pubsub-exchange", BuiltinExchangeType.FANOUT);
        channel.queueBind("pubsub-queue1","pubsub-exchange","");
        channel.queueBind("pubsub-queue2","pubsub-exchange","");
        String msg = "Hello-World";
        //发布消息到exchange,同时指定路由规则
         /**
        /**
         * 参数1 : 指定exchange  ""表示使用默认的exchange
         * 参数2 : 指定路由规则,使用具体的队列名称
         * 参数3 : 指定传递的消息所携带的properties
         * 参数4 : 指定发布的具体消息 byte[]类型
         * PS : exchange不会把消息持久化到本地,Queue才会持久化消息
         */
        for (int i = 0; i < 10; i++) {
            String msg = "pubsub" + i;
            channel.basicPublish("pubsub-exchange","Work",null,msg.getBytes());
        }
        System.out.println("生产者发布消息成功!");
        //释放资源
        channel.close();
        connection.close();
    }
}

对于交换机的类型,在RabbitMQ入门篇中也有详细描述

而消费者中的代码基本不变,只需要把队列名称对应修改即可

依次启动消费者,生产者    两个消费者都消费十条

4、Routing

它是一种完全按照routing key(路由关键字)进行投递的:当消息中的routing key和队列中的binding key完全匹配时,才进行会将消息投递到该队列中

Publisher

public class Publisher {

    @Test
    public void publish() throws IOException, TimeoutException {
        //获取Connection
        Connection connection = RabbitMQClient.getConnection();
        //创建Channel
        Channel channel = connection.createChannel();
        //创建exchange  routing-queue-info routing-queue-error
        channel.exchangeDeclare("routing-exchange", BuiltinExchangeType.DIRECT);
        //绑定queue
        channel.queueBind("routing-queue-error","routing-exchange","ERROR");
        channel.queueBind("routing-queue-info","routing-exchange","INFO");
        //发布消息到exchange,同时指定路由的规则
        channel.basicPublish("routing-exchange","ERROR",null,"ERROR".getBytes());
        channel.basicPublish("routing-exchange","INFO",null,"INFO1".getBytes());
        channel.basicPublish("routing-exchange","INFO",null,"INFO2".getBytes());
        channel.basicPublish("routing-exchange","INFO",null,"INFO3".getBytes());
        System.out.println("生产者发布消息成功!");
        //释放资源
        channel.close();
        connection.close();
    }
}

Comsumer

public class Consumer1 {

    @Test
    public void consume() throws IOException, TimeoutException {
        Connection connection = RabbitMQClient.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("routing-queue-error",true,false,false,null);

        //指定当前消费者,一次消费多少条消息
        channel.basicQos(1);
        //开启监听Queue
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者ERROR接收到消息:" + new String(body,"UTF-8"));
                //手动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        /**
         * 参数1:指定消费哪个队列
         * 参数2:指定是否自动ACK(true,接收到消息后,会立即告诉RabbitMQ)
         * 参数3:指定消费回调
         */
        channel.basicConsume("routing-queue-error",false,defaultConsumer);
        System.in.read();
        //释放资源
        channel.close();
        connection.close();

    }
}

消费者INFO就不贴了,把对应的队列名称修改即可

如此不同的消费者只会接收属于自己的消息

5、Topics

topic exchangedirect exchange类似,都是通过routing keybinding key进行匹配,不同的是topic exchange可以为routing key设置多重标准。

direct路由器类似于sql语句中的精确查询;topic 路由器有点类似于sql语句中的模糊查询。

Direct:完全根据key进行投递的,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。

Topic:对key进行模式匹配后进行投递,符号”#”匹配一个或多个词,符号”*”匹配正好一个词。例如”abc.#”匹配”abc.def.ghi”,”abc.*”只匹配”abc.def”。

Publisher

public class Publisher {

    @Test
    public void publish() throws IOException, TimeoutException {
        //获取Connection
        Connection connection = RabbitMQClient.getConnection();
        //创建Channel
        Channel channel = connection.createChannel();
        //创建exchange绑定队列  topic-queue1  topic-queue2
        channel.exchangeDeclare("topic-exchange", BuiltinExchangeType.TOPIC);
        channel.queueBind("topic-queue1","topic-exchange","*.red.*");
        channel.queueBind("topic-queue2","topic-exchange","fast.#");
        channel.queueBind("topic-queue2","topic-exchange","*.*.rabbit");
        //发布消息到exchange
        channel.basicPublish("topic-exchange","fast.red.dog",null,"很快红色猴子".getBytes());
        channel.basicPublish("topic-exchange","slow.black.cat",null,"黑慢猫".getBytes());
        channel.basicPublish("topic-exchange","fast.white.rabbit",null,"快白兔".getBytes());
        System.out.println("生产者发布消息成功!");
        //释放资源
        channel.close();
        connection.close();
    }
}

Consumer1

public class Consumer1 {

    @Test
    public void consume() throws IOException, TimeoutException {
        Connection connection = RabbitMQClient.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("topic-queue1",true,false,false,null);

        //指定当前消费者,一次消费多少条消息
        channel.basicQos(1);
        //开启监听Queue
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("1号消费者喜欢红色动物:" + new String(body,"UTF-8"));
                //手动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        /**
         * 参数1:指定消费哪个队列
         * 参数2:指定是否自动ACK(true,接收到消息后,会立即告诉RabbitMQ)
         * 参数3:指定消费回调
         */
        channel.basicConsume("topic-queue1",false,defaultConsumer);
        System.in.read();
        //释放资源
        channel.close();
        connection.close();

    }
}

Consumer2使用topic-queue2

猜你喜欢

转载自blog.csdn.net/QingXu1234/article/details/115599154