我第一次使用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
类)被调用时它们将被传递?
感谢您的帮助!
答案 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) {
...