Java rabbitmq交换路由密钥意外匹配

Java rabbitmq交换路由密钥意外匹配,java,queue,rabbitmq,Java,Queue,Rabbitmq,我正在为M2M解决方案开发RabbitMQ的POC。我有大量的物理设备将发布数据(目前使用Java客户机模拟客户机-最终通过MQTT)。我想: 订阅并记录数据库中的所有原始数据 按数据类型订阅数据子集,这样我就可以进行缩放 独立地为这些类型的数据提供解决方案 通过exchange发布新事件(例如,获取原始事件、制作 它更有用,并通过系统重新提交) 每条消息都有一个路由键,如key:value.key:value.key:value.messageType:1,来自设备的数据有一个额外的FROMD

我正在为M2M解决方案开发RabbitMQ的POC。我有大量的物理设备将发布数据(目前使用Java客户机模拟客户机-最终通过MQTT)。我想:

  • 订阅并记录数据库中的所有原始数据
  • 按数据类型订阅数据子集,这样我就可以进行缩放 独立地为这些类型的数据提供解决方案
  • 通过exchange发布新事件(例如,获取原始事件、制作 它更有用,并通过系统重新提交)
  • 每条消息都有一个路由键,如key:value.key:value.key:value.messageType:1,来自设备的数据有一个额外的FROMDEVICE键。messageType:1.key:value。。。等。保存设备原始数据的订户使用路由密钥#.FROMDEVICE.#(上面的案例#1)从交换机构建队列。接受特定消息类型并添加值的订阅者使用路由键#构建一个队列。消息类型:1.#(上面的案例#2)并向同一交换机提交一条新消息,从路由键中删除并替换。消息类型:1为。消息类型:101(上面的案例#3)。然后,新消息类型有一个独立的订阅者/队列

    一切都很好,除了我的订阅者(应该只从设备接收数据)也在获取增值数据(MESSAGETYPE:101),即使它应该搜索的routingKey在重新发布/增值消息中不存在

    • 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重新实现,不再遇到这个问题。非常感谢。