RabbitMQ学习记录 - WorkQueue(工作队列)

(内容均来自RabbitMQ官网:https://www.rabbitmq.com/tutorials/tutorial-two-java.html

上一篇学习了RabbitMQ的"HelloWorld",导入AMQP的客户端依赖,然后封装一个获取RabbitMQ连接的简单工厂工具类,这一篇我们就学习下官方的WorkQueue(工作队列)。和HelloWorld不同之处就是WorkQueue从 一个消费者变成了多个。

图例

生产者

        我们申明了一个为hello的队列,然后用for循环往hello队列中发送了20条内容为:{i}Hello RabbitMQ的信息

import com.booyue.tlh.utils.RabbitMQUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;

public class Producer  {

    public static final String QUEUE_NAME = "hello";
    private static final String MESSAGE = " Hello RabbitMQ";

    public static void main(String[] args) throws IOException {
        //获取一个连接
        Connection connection = RabbitMQUtils.getConnection();
        //获取一个通道
        Channel channel = connection.createChannel();
        //声明一个队列,名字为“hello”
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //往“hello”队列里发送20条"{i} Hello RabbitMQ"的信息
        for (int i = 0; i < 20; i++) {
            channel.basicPublish("", QUEUE_NAME, null, (i+MESSAGE).getBytes());
        }
    }
}

消费者01和消费者02

        消费者01和02不同点就是在模拟它们各自的实际工作的耗时上不一样,消费者01的工作耗时为1秒,消费者02的工作耗时为2秒(这里只列出了Consumer01,Consumer02不同之处就是Thread.sleep(2000))

import com.booyue.tlh.utils.RabbitMQUtils;
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

@Slf4j
public class Consumer01 {
    public static void main(String[] args) throws IOException {
        //获取连接
        Connection connection = RabbitMQUtils.getConnection();
        //获取通道
        Channel channel = connection.createChannel();
        //这里也可以声明一个和生产者里面声明的一样的队列,不然如果先启动消费这的话系统会报错,说系统没有对应的queue。多出声明也没关系,第一次声明之后系统就不会在创建对应的queue了
        channel.queueDeclare(Producer.QUEUE_NAME, false, false, false, null);
        /**
         * 接收消息
         *  参数1:队列名
         *  参数2:是否自动确认
         *  参数3:消息回调接口
         */
        channel.basicConsume(Producer.QUEUE_NAME, true, new DefaultConsumer(channel) {
            @SneakyThrows
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
                String message = new String(body, "utf-8");
                try {
                    //模拟完成业务逻辑的耗时
                    Thread.sleep(1000);
                } finally {
                    log.info("消费者1收到的信息:{}", message);
                }
            }
        });
    }
}

启动生产者和两个消费者,查看两个消费者收到的消息

10:41:00.862 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:1 Hello RabbitMQ
10:41:01.866 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:3 Hello RabbitMQ
10:41:02.867 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:5 Hello RabbitMQ
10:41:03.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:7 Hello RabbitMQ
10:41:04.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:9 Hello RabbitMQ
10:41:05.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:11 Hello RabbitMQ
10:41:06.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:13 Hello RabbitMQ
10:41:07.870 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:15 Hello RabbitMQ
10:41:08.870 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:17 Hello RabbitMQ
10:41:09.871 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:19 Hello RabbitMQ


10:41:00.861 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:0 Hello RabbitMQ
10:41:01.865 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:2 Hello RabbitMQ
10:41:02.866 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:4 Hello RabbitMQ
10:41:03.867 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:6 Hello RabbitMQ
10:41:04.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:8 Hello RabbitMQ
10:41:05.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:10 Hello RabbitMQ
10:41:06.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:12 Hello RabbitMQ
10:41:07.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:14 Hello RabbitMQ
10:41:08.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:16 Hello RabbitMQ
10:41:09.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:18 Hello RabbitMQ

我们看到尽管两个消费者的工作耗时即使不一样,它们两个收到的消息数量都是一样的。原因是在自动确认的情况下,系统会默认按照顺序将每一条消息发送给下一个消费者,平均而言每个消费者会获得相同数量的消息,这种方式称为:轮询。也就是说,不管你的众多消费者中其中某一个或者多个的压力有多大,它获得的消息和其他消费者都是一样的(除非消费者关闭通道或者断开连接)。但是现实中,我们多个消费者可能分布在多态机器上,有的机器性能好有的性能差,很明显这种方式不是很合理。

公平调度

        我们可以采用公平调度的方式,根据消费者处理消息的能力来接受消息的数量。

图例

生产者

        和上面的生产者一样用for循环往hello队列中发送了20条内容为:{i}Hello RabbitMQ的信息

消费者01和消费者02

        和上面不同的地方就是在接受消息之前告诉系统一次发送一条信息( channel.basicQos(1))和不采用自动确认的方式(autoAck=false),等完成处理操作之后告诉服务器(channel.basicAck(envelope.getDeliveryTag(), false))

import com.booyue.tlh.utils.RabbitMQUtils;
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

@Slf4j
public class Consumer01 {
    public static void main(String[] args) throws IOException {
        //获取连接
        Connection connection = RabbitMQUtils.getConnection();
        //获取通道
        Channel channel = connection.createChannel();
        //这里也可以声明一个和生产者里面声明的一样的队列,不然如果先启动消费这的话系统会报错,说系统没有对应的queue。多出声明也没关系,第一次声明之后系统就不会在创建对应的queue了
        channel.queueDeclare(Producer.QUEUE_NAME, false, false, false, null);
        //每次给消费者发送一条系信息,直到他告诉RabbitMQ服务器已经完成该条消息,才会收到下一条消息
        channel.basicQos(1);
        /**
         * 接收消息
         *  参数1:队列名
         *  参数2:是否自动确认
         *  参数3:消息回调接口
         */
        channel.basicConsume(Producer.QUEUE_NAME, false, new DefaultConsumer(channel) {
            @SneakyThrows
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
                String message = new String(body, "utf-8");
                try {
                    //模拟完成业务逻辑的耗时
                    Thread.sleep(1000);
                } finally {
                    log.info("消费者1收到的信息:{}", message);
                    //拿到标识,确认己经收到该条信息
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        });
    }
}

启动生产者和两个消费者,查看两个消费者收到的消息

        很明显,由于消费者01的工作能力强(耗时只有1秒),它处理消息的数量也就多

11:23:41.806 [pool-1-thread-16] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:0 Hello RabbitMQ
11:23:42.806 [pool-1-thread-17] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:2 Hello RabbitMQ
11:23:43.806 [pool-1-thread-18] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:3 Hello RabbitMQ
11:23:44.807 [pool-1-thread-19] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:5 Hello RabbitMQ
11:23:45.808 [pool-1-thread-20] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:7 Hello RabbitMQ
11:23:46.808 [pool-1-thread-21] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:8 Hello RabbitMQ
11:23:47.808 [pool-1-thread-22] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:10 Hello RabbitMQ
11:23:48.810 [pool-1-thread-23] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:11 Hello RabbitMQ
11:23:49.810 [pool-1-thread-24] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:13 Hello RabbitMQ
11:23:50.811 [pool-1-thread-25] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:14 Hello RabbitMQ
11:23:51.812 [pool-1-thread-26] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:16 Hello RabbitMQ
11:23:52.812 [pool-1-thread-3] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:17 Hello RabbitMQ
11:23:53.813 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:19 Hello RabbitMQ




11:23:42.806 [pool-1-thread-18] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:1 Hello RabbitMQ
11:23:44.807 [pool-1-thread-19] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:4 Hello RabbitMQ
11:23:46.808 [pool-1-thread-20] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:6 Hello RabbitMQ
11:23:48.809 [pool-1-thread-21] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:9 Hello RabbitMQ
11:23:50.809 [pool-1-thread-22] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:12 Hello RabbitMQ
11:23:52.809 [pool-1-thread-23] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:15 Hello RabbitMQ
11:23:54.810 [pool-1-thread-24] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:18 Hello RabbitMQ

其他

        还是上一篇说到的,例子中没有看到交换机(Exchange)的影子,但是我们心里一定要清楚,系统一定是有使用交换机的。为我们后面更好的理解RabbitMQ的整个流程做一定的铺垫。

Guess you like

Origin blog.csdn.net/qq_27062249/article/details/118541042