MQtt向某一设备推送消息
先说明下背景:emq集群为2个节点,配置为4核16G,同时在线设备量大概在10W左右(即每个节点负载约5W)。
需求就是:需要向这其中的某3万设备推送某个消息。
解读下需求:比如我现在有设备号码是 1-100000 的这么些设备,要推送 30000-60000 的这些设备一些消息。
怎么实现呢?
目前有两种思路,且听我一一道来。
思路一:利用emq的保留消息实现。这也是我第一次想到并付诸于实践的思路。
保留消息的特点是,发布到topic的消息如果是保留消息的话,那么订阅这个topic的设备上线后将收到此消息。
那么可以这样设计:
设备端,每个设备都订阅自己独有的topic,例如用自己唯一的设备号,比如
1号设备订阅:private/1
2号设备订阅:private/2
这样在发布的时候,就可以向上面 private/30000 到 private/60000 这些topic发布保留消息,然后也只有对应订阅了这些topic的设备能收到了。
但是,嘿嘿,凡事都有但是。这种实现有两个问题:
1,鉴于保留消息的机制(一个topic只保留一条最新消息),同时只能像某个设备推送一条消息,举个例子:
在向 private/1 发了保留消息推送第一条消息【1号1号,再不上线就分手!】时,若1号设备一直不在线,而这时候你等不及又推了第二条消息【都是你的错!分手吧!】。这时如果1号设备联网上线了,就只会收到第二条消息,到最后也不知道为啥被分了手。
2,emq服务器在上述背景下,推送几千条的保留消息,就会把cpu跑满!这个问题,我反馈咨询过emq的开发人员,emq存数据是用的mnesia数据库,如果有大量保留消息的时候,每个设备上线时,都会到这个库查看是否有此设备所订阅topic匹配的路由数据。我的理解是,emq在存在大量保留消息,且上下线频繁的场景下,还做的不够好。
思路二:利用 redis+emq的上下线数据 实现。
鉴于保留消息有以上的问题,肯定行不通了,后来有想到一种思路。解决了这个问题。
设计思路:
设备端,每个设备订阅自己独有的topic,这个不变。
而发布端,不再发保留消息到emq,而是发到redis。在redis内,为每个设备维护一个待推送数据列表。利用redis的set数据结构来实现。比如:
1号设备的待推送列表为set结构的,key的结构为:retain:1
然后往这个key里面存要发布的指令,然后再利用emq的上下线消息,一上线,就去redis查 retain:1 下有没有待推送数据,有就推送,这样就实现了向独立设备推送独立消息的需求。推完之后可以删掉此消息,或者再设计一个回执反馈,待设备发回了某些特定回执之后再删也可以。并且,set结构的key能够存多条数据,只要内容不一样,所以可以解决上面保留消息第一点的问题。
至于上面第二点的问题,redis数据存在内存,吞吐极大,性能没有问题。
以上,笔者用第二种方式实现了上述背景下的需求,经过长时间验证,性能和可持久性都可靠。
资源猫 » MQtt向某一设备推送消息