在消费者正在收听消息之前发送消息时,消费者不会从MQ接收消息

时间:2013-08-22 21:00:13

标签: java rabbitmq mq

我第一次使用MQ并尝试使用RabbitMQ实现日志记录系统。我的实施涉及“发件人”

/*
 * This class sends messages over MQ
 */
public class MQSender {
    private final static String EXCHANGE_NAME = "mm_exchange";
    private final static String[] LOG_LEVELS = {"green", "orange", "red", "black"};

    public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
        /*
         * Boilerplate stuff
         */
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        //declare the exchange that messages pass through, type=direct
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");

        String[] levels = {"green", "orange", "red", "black"};
        for (String log_level : levels) {
            String message = "This is a " + log_level + " message";
            System.out.println("Sending " + log_level + " message");
            //publish the message with each of the bindings in levels
            channel.basicPublish(EXCHANGE_NAME, log_level, null, message.getBytes());
        }

        channel.close();
        connection.close();
    }
}

将每种颜色的一条消息发送到交换机,其中颜色将用作绑定。它涉及一个'接收器'

public class MQReceiver {
    private final static String EXCHANGE_NAME = "mm_exchange";
    private final static String[] LOG_LEVELS = {"green", "orange", "red", "black"};

    public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
        receiveMessagesFromQueue(2);
    }

    public static void receiveMessagesFromQueue(int maxLevel) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
        /*
         * Boilerplate stuff
         */
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        //declare the exchange that messages pass through, type=direct
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");

        //generate random queue
        String queueName = channel.queueDeclare().getQueue();

        //set bindings from 0 to maxLevel for the queue
        for (int level = 0; level <= maxLevel; level++) {
            channel.queueBind(queueName, EXCHANGE_NAME, LOG_LEVELS[level]);
        }

        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(queueName, true, consumer);

        while(true) {
            //waits until a message is delivered then gets that message
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            String routingKey = delivery.getEnvelope().getRoutingKey();

            System.out.println(" [x] Received '" + routingKey + "':'" + message + "'");
        }
    }
}

作为参数给出一个数字,表示我希望从交换机输入的颜色绑定。

在我的实现中,在RabbitMQ中,似乎消息存储在交换中,直到Consumer请求它们,此时它们被分发到各自的队列,然后一次发送一个到客户端(或MQ术语中的消费者)。我的问题是,当我在运行MQSender类之前运行MQReceiver类时,消息永远不会被传递。但是当我首先运行MQReceiver类时,会收到消息。根据我对MQ的理解,我认为消息应该存储在服务器上,直到MQReceiver类运行,然后消息应该传递给他们的消费者,但这不是正在发生的事情。我的主要问题是这些消息是否可以存储在交换中,如果没有,那么它们应该存储在何处,以便在消费者(即我的MQReceiver类)被调用时它们将被传递?

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

如果路由密钥与绑定到交换机的任何队列都不匹配,RabbitMQ会丢弃消息。首先启动MQSender时,不会绑定任何队列,因此它发送的消息将丢失。当您启动MQReceiver时,它会将队列绑定到交换机,因此RabbitMQ可以放置来自MQSender的消息。当您停止MQReceiver时,由于您创建了匿名队列,因此将从交换中删除队列和所有绑定。

如果希望在MQReceiver未运行时将消息存储在服务器上,则需要接收方创建命名队列,并将路由密钥绑定到该队列。请注意,创建命名队列是幂等的,如果队列已存在,则不会创建队列。然后,您需要接收器从指定队列中提取消息。

将代码更改为如下所示:

<强> MQSender

....
String namedQueue = "logqueue";
//declare named queue and bind log level routing keys to it.
//RabbitMQ will put messages with matching routing keys in this queue
channel.queueDeclare(namedQueue, false, false, false, null);
for (int level = 0; level < LOG_LEVELS.length; level++) {
   channel.queueBind(namedQueue, EXCHANGE_NAME, LOG_LEVELS[level]);
}
...

<强> MQReceiver

...
channel.exchangeDeclare(EXCHANGE_NAME, "direct");

QueueingConsumer consumer = new QueueingConsumer(channel);

//Consume messages off named queue instead of anonymous queue
String namedQueue = "logqueue";
channel.basicConsume(namedQueue, true, consumer);

while(true) {
...