Java rabbitmq交换路由密钥意外匹配
我正在为M2M解决方案开发RabbitMQ的POC。我有大量的物理设备将发布数据(目前使用Java客户机模拟客户机-最终通过MQTT)。我想:Java rabbitmq交换路由密钥意外匹配,java,queue,rabbitmq,Java,Queue,Rabbitmq,我正在为M2M解决方案开发RabbitMQ的POC。我有大量的物理设备将发布数据(目前使用Java客户机模拟客户机-最终通过MQTT)。我想: 订阅并记录数据库中的所有原始数据 按数据类型订阅数据子集,这样我就可以进行缩放 独立地为这些类型的数据提供解决方案 通过exchange发布新事件(例如,获取原始事件、制作 它更有用,并通过系统重新提交) 每条消息都有一个路由键,如key:value.key:value.key:value.messageType:1,来自设备的数据有一个额外的FROMD
- FROMDEVICE.MESSAGETYPE:1->
- 应与设备中的路由密钥#匹配#
- 应匹配#。消息类型:1#
- 消息类型:101
- 应与路由键#匹配。消息类型:101#
- 不应与#.FROMDEVICE.#匹配(但匹配)
public class HandlerWriteEverythingFromDevice {
private final static String EXCHANGE_NAME = "logsTopicDurable";
private final static String QUEUE_NAME = "fromDevice";
/**
* Writes all data from device to a data store.
*/
public static void main(String[] args) throws java.io.IOException, java.lang.InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.101");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
System.out.println(" [*] listens for messages from devices - durable!");
channel.basicQos(1);
String routingKey = "#.fromDevice.#".toUpperCase();
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey); //bind to all selected messages
System.out.println(" [*] subscribing to: " + routingKey);
System.out.println(" [*] Waiting for messages. To exit press CTRL_C");
QueueingConsumer consumer = new QueueingConsumer(channel);
boolean autoAck = false; //ack back when done
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
int msgCount = 0;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Message Count: " + ++msgCount + " ROUTINGKEY: '" + delivery.getEnvelope().getRoutingKey() + "\n MESSAGE: '" + message + "'");
Thread.sleep(250); //simulate some time to insert into the db.
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
仅订阅messageType:1并重新发布messageType:101的代码
private final static String EXCHANGE_NAME = "logsTopicDurable";
private final static String QUEUE_NAME = "messageType1";
/**
* Handler for messageType:1
*/
public static void main(String[] args) throws java.io.IOException, java.lang.InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.101");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
System.out.println(" [*] listens for messageType:1 and submits messageType:101");
channel.basicQos(1);
String routingKey = "#.messageType:1.#".toUpperCase();
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey); //bind to all selected messages
System.out.println(" [*] subscribing to: " + routingKey);
System.out.println(" [*] Waiting for messages. To exit press CTRL_C");
QueueingConsumer consumer = new QueueingConsumer(channel);
boolean autoAck = false; //ack back when done
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
int msgCount = 0;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Message Count: " + ++msgCount + " ROUTINGKEY: '" + delivery.getEnvelope().getRoutingKey() + "\n MESSAGE: '" + message + "'");
channel.basicPublish(EXCHANGE_NAME,
delivery.getEnvelope().
getRoutingKey().
replaceAll("messageType:1", "messageType:101").
replaceAll(".FROMDEVICE", "").
replaceAll("FROMDEVICE.", "").trim(),
true,
MessageProperties.PERSISTENT_BASIC,
message.getBytes());
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
messageType:101有发布者代码和订阅者代码,但我认为本次讨论不需要它们。我想知道发布到绑定了队列的频道是否是原因,但我尝试创建了两个频道(相同的连接对象),结果相同,代码也更难看。我建议您在绑定密钥方面有点过于自由。为了让事情更清楚一点,您应该以不同的方式使用术语绑定键和路由键。路由密钥是生产者发送的内容。绑定键用于将队列绑定到主题交换 因为我不能确定你说的是什么 “应与路由键#匹配。消息类型:101.#” 您是否使用路由键
#发送邮件。邮件类型:101.#
,因为这不是个好主意。我想不会,但如果你是的话,不要
假设这是您的绑定密钥。我不确定,因为我没有做任何测试,但前后的
可能会导致一些问题。您应该考虑路由密钥的规范。他们必须遵守的一些格式。它可以延伸,但不能完全自由。通过这种方式,您可以使用*
而不是#
来拥有更具体的绑定键,这将提供更多的控制。您是对的,我在混合我的术语。消息上的路由键为:“FROMDEVICE.MESSAGETYPE:1”,订阅上的绑定键为“#.FROMDEVICE.#”。我的问题是,路由密钥“MESSAGETYPE:101”与绑定密钥“#.FROMDEVICE.#”(在本例中,我希望使用路由密钥“FROMDEVICE.MESSAGETYPE:1”获取消息,更改它,然后使用路由密钥“MESSAGETYPE:101”重新发布消息,而不使用与绑定密钥“#.FROMDEVICE.#”)匹配的路由密钥1.我很好奇这场公开赛的效果如何。我在POC中还太早,不知道是否能够在路由密钥中强制执行顺序。我简化了我的示例代码,将路由键更改为始终从FROMDEVICE开始。。。(如果来自设备且未由其他消费者发布)。我还将绑定密钥更新为fromdevice.#以反映更改。结果是相同的-重新发布的消息仍然排队等待与路由密钥不匹配的订阅者。这可能是我使用同一渠道消费和发布消息的方式吗?还是WSO2缺陷?两个建议。您是否检查/打印/记录从设备接收的代码的路由键。。我有一种感觉,因为某种原因,替换可能不起作用。您是否已检查正在重新发送的路由密钥是否正确。是的,我已检查了两者。在上面的第一组代码中-在while循环中,我已经打印了我收到的密钥:System.out.println(“[x]消息计数:”+++msgCount++“ROUTINGKEY:””“+delivery.getEnvelope().getRoutingKey()+”\n消息:“+Message+”);。在控制台中,我可以看到路由密钥与我用于“从设备写入所有内容”的绑定密钥不匹配订阅。我和Rabbit的一些人谈过,他们看不出代码有任何明显的错误,但是他们建议删除路由密钥中的冒号,因为他们希望使用以句点分隔的a-z0-9字符。在尝试通过web套接字从STOMP创建绑定时,冒号确实给我带来了一些问题。我已经从AMQP示例开始,并为发布服务器使用MQTT重新实现,不再遇到这个问题。非常感谢。