如何通过基于云的系统强制执行通过MQTT传递给IoT设备的消息的顺序(API设计问题)

时间:2016-01-29 23:43:29

标签: protocols messaging mqtt iot

假设我有一个IoT设备,我即将控制(让我们说打开/关闭)和监控(例如收集温度读数)。似乎MQTT可能是合适的。我可以将消息发布到设备以控制它,并且设备可以向代理发布消息以报告温度读数。到目前为止一切都很好。

当我尝试设计API来控制设备时,问题就开始出现了。

让设备订阅两个主题:

  • /设备ID /控制/上
  • /设备ID /控制/关

然后我按照某种顺序向这些主题发布消息。但鉴于消息传递通常是异步过程,因此无法保证设备接收的消息顺序。

因此,如果按以下顺序发布两条消息:

  1. /设备ID /控制/上
  2. /设备ID /控制/关
  3. 他们可以以相反的顺序接收,让设备开启,这可能会产生严重后果,具体取决于具体情况。

    当然,API可以通过其他方式设计,例如,可能只有一个主题

    1. /设备ID /控制
    2. 并且各个消息的有效载荷将带有单个消息的含义(开/关)。因此,如果消息按给定顺序发布到此主题,则应在设备上以完全相同的顺序接收消息。

      但是,如果无法保证发布到个别主题的顺序怎么办?假设物联网设备的系统具有以下架构:

                             / control service \
      application -> broker -> control service -> broker -> IoT device
                             \ control service /
      

      系统的组成部分是:

      • 通过向经纪人发布消息来有效控制设备的应用程序
      • 典型的消息代理
      • 具有一些业务逻辑的控制服务

      重要的是,与大多数现代分布式系统一样,控制服务是一个分布式的多实例实体,能够一次处理来自应用程序的多个控制消息。因此,应用程序发布的消息顺序在传送到物联网设备时可能会完全混合。

      现在考虑到大多数MQTT代理只实现QoS0和QoS1但没有QoS2,因为这样的控制消息可能会被多次传递(假设QoS1 - 参见https://stackoverflow.com/a/30959058/1776942)。

      我的观点是控制消息的单独主题是一个坏主意。单个主题也是如此。在这两种情况下都没有邮件传递顺序保证。

      我想到的这个特定问题的唯一解决方案是消息版本控制,以便在具有更新版本属性的另一条消息之后传递时,可以简单地跳过旧的(过时的)消息。

      • 我错过了什么吗?
      • 邮件版本控制是此问题的唯一解决方案吗?

3 个答案:

答案 0 :(得分:5)

我错过了什么吗?

绝对是最好的。您提出的示例是一个通用控制系统,附加到一些面向消息的方案。在引用基于消息的体系结构时,可以使用许多模式。 Microsoft This article将邮件模式分为两个主要类:

  • 命令
  • 活动

最常见的命令行为模式是发出命令,然后测量系统状态以验证命令是否已执行。如果您忘记验证,您的系统有一个开环。这种开放循环(不幸的是)在IT系统中很常见(因为它很容易忘记),并且经常导致错误和其他不良行为,例如上面描述的错误行为。因此,处理命令的正确方法是:

  1. 发出命令
  2. 查询系统状态
  3. 评估下一步行动
  4. 另一方面,事件被简单地解雇了。作为一个事件的发布者,我不应该担心谁接收事件,以什么顺序等等。现在,还应该指出使用任何体面的消息代理(例如RabbitMQ)通常强有力地保证消息将按照最初发布的顺序传递。 Note that this does not mean they will be processed in order.

    因此,如果您将某个命令视为一个事件,您的系统将保证迟早会采取行动。

    邮件版本控制是此问题的唯一解决方案吗?

    消息版本控制通常是指消息类本身的属性,而不是类的特定实例。它通常在存在基于消息的API的多个版本时使用,并且必须向后兼容。

    您所指的是唯一的消息标识符。 Guids对于确保每封邮件都有自己唯一的ID非常方便。但是,我认为基于消息的体系结构中的重复数据删除是一种反模式。使用消息传递的一个后果是重复是可能的,因此您应该尝试将系统行为设计为无状态和idempotent。如果无法做到这一点,则应该认为消息传递可能不是满足需求的正确通信解决方案。

    使用命令事件二分法作为示例,您可以执行以下事务:

    1. 控制器发出命令,为命令分配唯一标识符。
    2. 控制系统接收命令并开启。
    3. 控制系统发布"灯光"事件通知,包含用于打开灯的命令的唯一ID。
    4. 控制器接收通知并将其与原始命令相关联。
    5. 如果控制器在某些超时后没有收到通知,控制器可以重试该命令。请注意"点亮"是一个幂等命令,因为对它的多次调用将产生相同的效果。

答案 1 :(得分:1)

当状态发生变化时,立即发送新状态,然后每隔x秒定期发送一次。使用此解决方案,您的系统会在一段时间后进入所需状态,即使它暂时断开网络连接(电池电量不足)。

顺便说一句:你没有错过任何东西。

答案 2 :(得分:1)

除了大多数经纪人不支持QOS2的评论之外(我怀疑你的意思是许多经纪人作为服务产品不支持QOS2,例如亚马逊的AWS物联网服务)你已经涵盖了大部分要点。

如果消息顺序非常重要,那么您必须在消息有效负载中包含某种形式的排序标记,这是一个计数器或时间戳。