最近打算把自助洗车项目部署到云上,根据流量自行扩展/缩减服务以保障高可用,以及防范DDOS/CC等攻击。可这样就会出现一个问题,多端的情况下,如何保障不重复,只有一个客户端消费了消息,经过查询资料,想出了几种方案。
1. 在业务代码中自行处理
- 每条消息记录唯一的ID。
- 然后每次消费前判断是否该ID已经消费。
- 具体代码就请自行实现。
- 优点是该方案定制性强
- 缺点是处理太过麻烦,得记录ID,且判断是否消费前得加锁/释放锁等操作,高并发性能差。
2. EMQX 共享订阅(最优方案)
官网介绍
共享订阅是在多个订阅者之间实现负载均衡的订阅方式,EMQX 在 MQTT v3.1.1 中已经实现共享订阅共享订阅,MQTT v5.0 协议中这一特性成为标准的一部分。
共享订阅能够解决以下问题:
集群模式下,如果订阅者所在的节点发生故障,则发布者的消息会丢失(QoS 0)或者堆积在节点中(QoS 1, 2)。可以通过增加订阅节点的方式解决这一问题,但这样又产生了大量的重复消息浪费了性能,并增加了业务的复杂度。
当发布者的生产能力较强时,可能会出现订阅者的消费能力无法及时跟上的情况,此时只能由订阅者自行实现负载均衡来解决,又一次增加了用户的开发成本。
上图中,共享 3 个 subscriber 用共享订阅的方式订阅了同一个主题 $share/g/topic,其中topic 是它们订阅的真实主题名,而 $share/g/ 是共享订阅前缀。EMQX 支持两种格式的共享订阅前缀:
示例 | 前缀 | 真实主题名 |
---|---|---|
$share/abc/t/1 | $share/abc/ | t/1 |
- 该方案就是多个订阅只会发送给其中一个节点,实现简单,最为推荐。
3. 排他性订阅
示例 | 前缀 | 真实主题名 |
---|---|---|
$exclusive/t/1 | $exclusive/ | t/1 |
排它订阅允许对主题进行互斥订阅,一个主题同一时刻仅被允许存在一个订阅者,在当前订阅者未取消订阅前,其他订阅者都将无法订阅对应主题。
当某个客户端 A 订阅 $exclusive/t/1 后,其他客户端再订阅 $exclusive/t/1 时都会失败,直到 A 取消了对 $exclusive/t/1 的订阅为止。
注意: 排它订阅必须使用 $exclusive/ 前缀,在上面的示例中,其他客户端依然可以通过 t/1 成功进行订阅。
- 该方案作为知识点即可,因为如果订阅失败的话,一般情况下程序会自动重连,这样会导致云上费用增加,且实现不合理。
有一个点一定要记住,emqx支持同一用户登录多个,但是client id一定要不同,所以同一套代码在需要部署分布式时client-id最好配置为随机uuid。
# auth 157239486@qq.com
# java application.yml配置示例
client-id: ${random.uuid}