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)保留的消息。之后,实例化MqttSubscriber
和IMqttMessageListener
接口的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/
cleanSession=true
,因为我不想拥有任何状态(该连接用于订阅多个主题,并且不希望传递错过的消息)答案 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:必须是假的。 除此之外,测试代码中的等待状态也很糟糕。它们在我的机器上太短了。使用锁存器或其他实际同步机制。