MQTT保留了同时从不同应用程序订阅相同主题时未收到的消息

时间:2018-02-13 14:34:57

标签: java mqtt paho hivemq emq

TL; DR

当同时订阅多个客户的同一主题树时,并非所有客户端都按预期收到保留的消息!

详细/用例

在一个真实的项目中,有几个应用程序订阅(几乎同时因为它们是并行启动的)同一个MQTT主题(使用通配符)。该主题包含大约500个保留消息(每个消息都在一个自己的子主题级别),所有应用程序都希望接收这些消息(它们使用QoS 1进行订阅)。

除了“配置”消息之外,数据主题也使用相同的MQTT连接进行订阅。不需要持久化状态(并且需要这里)。因此,应用程序实例与cleanSession=true连接。

根据我的理解,如果应用程序实例每个都与固定的clientId连接就足够了,因为 cleanSession = true 应该避免任何状态处理。但是要确定没有考虑任何状态,每个连接都会生成unique MQTT clientId

观察行为

不幸的是,并非所有应用程序实例都会收到保留的消息。有些消息从主题中得不到任何消息 - 无论订阅持续多长时间。我首先想到maxInflight(客户端)或max_queued_messages(服务器端)配置可能是原因,但是在将两者都增加到500,000之后,我猜这不是失败背后的原因。

再现为测试

因此我用repro创建了this github项目。 repro MqttSubscriptionTest中有一个单元测试类,其测试方法为multiThreadSubscriptionTest。执行此测试时,将首先在@BeforeClass方法中发布一些(1000)保留的消息。之后,实例化MqttSubscriberIMqttMessageListener接口的Runnable类的10个实例将被实例化并执行。每个MqttSubscriber实例将在具有自己的MqttClient实例的自己的线程中执行,并将使用保留的消息订阅主题树。这将按如下方式记录到控制台:

----------- perform subscriptions
Subscriber-3 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-0 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-2 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-4 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-5 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-6 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-1 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-7 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-8 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'
Subscriber-9 subscribing topic 'mqtt/client/showcase/mutliThreadSubscription/#'

测试将等待一段时间,之后验证订阅。预计每个订阅者都会收到1000条保留的消息:

----------- validate subscriptions
Subscriber-4: receivedMessages=1000; duration=372ms; succeeded=true
Subscriber-0: receivedMessages=1000; duration=265ms; succeeded=true
Subscriber-5: receivedMessages=1000; duration=475ms; succeeded=true
Subscriber-7: receivedMessages=0; duration=0ms; succeeded=false
Subscriber-6: receivedMessages=1000; duration=473ms; succeeded=true
Subscriber-8: receivedMessages=0; duration=0ms; succeeded=false
Subscriber-9: receivedMessages=1000; duration=346ms; succeeded=true
Subscriber-3: receivedMessages=1000; duration=243ms; succeeded=true
Subscriber-1: receivedMessages=1000; duration=470ms; succeeded=true
Subscriber-2: receivedMessages=1000; duration=357ms; succeeded=true

大多数用户在很短的时间内(几百毫秒)收到了预期的1000条消息。但有些(此处为Subscriber-7/8)未收到单个消息(持续时间为0,因为它们从未完成)。在给订户更多时间接收消息时,情况并不好。他们只是不会得到它们。

我不知道为什么会这样。 MQTT代理或客户端上未显示任何错误消息。如果你能提供任何帮助,这对我来说非常有用,因为我依赖于保留信息的可靠传递。

在GitHub上重播FrVaBe/MQTT/mqtt-client-showcase/

  • 我使用本地EMQ和HiveMQ经纪人进行了测试。如果要运行测试,则需要在localhost:1883上的计算机上运行代理,或者更改测试类中的配置。
  • 我使用Eclipse Paho Java Client MQTT
  • 我订阅了cleanSession=true,因为我不想拥有任何状态(该连接用于订阅多个主题,并且不希望传递错过的消息)

2 个答案:

答案 0 :(得分:4)

HiveMQ的人很友好地看看这个问题。他们怀疑Paho客户端中的原因以及订阅中IMqttMessageListener的使用。描述了种族条件的问题#432

获得的经验教训:更好地使用MqttCallback代替IMqttMessageListener

答案 1 :(得分:0)

您在连接期间不能使用cleanSession(true),请参阅此处的说明:http://www.steves-internet-guide.com/mqtt-retained-messages-example/

我使用mosquito进行测试,默认情况下它的内部消息队列只有100个。

HiveMQ有最大排队消息。

Emq配置有类似http://emqtt.io/docs/v2/config.html#mqtt-message-queue的内容。

我修复了repo中的示例代码。适合我。

编辑:刚刚使用Emq(docker run --rm -ti --name emq -p 18083:18083 -p 1883:1883 quodt/emq-docker:latest)进行了测试,效果很好。

基本上,它是cleanSession:必须是假的。 除此之外,测试代码中的等待状态也很糟糕。它们在我的机器上太短了。使用锁存器或其他实际同步机制。