在 IoT 中心内了解并使用模块孪生

本文假设读者事先已阅读了解并在 IoT 中心内使用设备孪生。 在 IoT 中心的每个设备标识下,最多可以创建 50 个模块标识。 每个模块标识隐式生成模块孪生。 模块孪生与设备孪生类似,是存储模块状态信息(例如元数据、配置和条件)的 JSON 文档。 Azure IoT 中心为连接到 IoT 中心的每个模块保留一个模块孪生。

在设备端,可以使用 IoT 中心设备 SDK 创建模块,其中每个模块与 IoT 中心建立独立连接。 通过此功能,可对设备上的不同组件使用不同的命名空间。 例如,某个自动贩卖机包含三个不同的传感器。 每个传感器由公司的不同部门控制。 可为每个传感器创建一个模块。 这样,每个部门只能向他们控制的传感器发送作业或直接方法,避免发生冲突和用户失误。

模块标识和模块孪生提供的功能与设备标识和设备孪生相同,但前者的粒度更细。 这种更高的粒度级可让有能力的设备(例如基于操作系统的设备,或管理多个组件的固件设备)隔离其中每个组件的配置和状态。 与包含模块化软件组件的 IoT 设备结合使用时,模块标识和模块孪生能够提供管理关注点分离。 推出模块孪生的正式版后,我们将会支持模块孪生级别的所有设备孪生功能。

注意

本文所述的功能只能用于 IoT 中心的标准层。 有关 IoT 中心基本层和标准/免费层的详细信息,请参阅选择适合你的解决方案的 IoT 中心层

本文介绍:

  • 模块孪生的结构:标记、所需的属性和报告的属性 。
  • 模块和后端可在模块孪生上执行的操作。

有关使用报告的属性、设备到云的消息或文件上传的指导,请参阅设备到云的通信指南

有关使用所需的属性、直接方法或云到设备的消息的指导,请参阅云到设备的通信指南

模块孪生

模块孪生存储具有以下用途的模块相关信息:

  • 设备和 IoT 中心上的模块可以使用这些信息来同步模块状态和配置。

  • 解决方案后端可以使用这些信息来查询和定位长时间运行的操作。

模块孪生的生命周期链接到相应的模块标识。 在 IoT 中心创建或删除模块标识时,将隐式创建和删除模块孪生。

模块孪生是一个 JSON 文档,其中包含:

  • 标记。 解决方案后端可从中读取和写入数据的 JSON 文档的某个部分。 标记对设备上的模块不可见。 设置的标记用于查询目的。

  • 所需的属性。 与报告的属性结合使用,同步模块配置或状态。 解决方案后端可设置所需的属性,并且模块应用可进行读取。 此外,当所需的属性发生更改时,模块应用可收到通知。

  • 报告的属性。 与所需的属性结合使用,同步模块配置或状态。 模块应用可设置报告的属性,并且解决方案后端可进行读取和查询。

  • 模块标识属性。 模块孪生 JSON 文档的根包含标识注册表中存储的相应模块标识的只读属性。

Architectural representation of device twin

以下示例显示了一个模块孪生 JSON 文档:

{
    "deviceId": "devA",
    "moduleId": "moduleA",
    "etag": "AAAAAAAAAAc=", 
    "status": "enabled",
    "statusReason": "provisioned",
    "statusUpdateTime": "0001-01-01T00:00:00",
    "connectionState": "connected",
    "lastActivityTime": "2015-02-30T16:24:48.789Z",
    "cloudToDeviceMessageCount": 0, 
    "authenticationType": "sas",
    "x509Thumbprint": {     
        "primaryThumbprint": null, 
        "secondaryThumbprint": null 
    }, 
    "version": 2, 
    "tags": {
        "deploymentLocation": {
            "building": "43",
            "floor": "1"
        }
    },
    "properties": {
        "desired": {
            "telemetryConfig": {
                "sendFrequency": "5m"
            },
            "$metadata" : {...},
            "$version": 1
        },
        "reported": {
            "telemetryConfig": {
                "sendFrequency": "5m",
                "status": "success"
            },
            "batteryLevel": 55,
            "$metadata" : {...},
            "$version": 4
        }
    }
}

根对象中包含模块标识属性,以及 tagsreporteddesired 属性的容器对象。 properties 容器包含一些只读元素($metadata$version),模块孪生元数据乐观并发部分描述了这些元素。

报告属性示例

在上面的示例中,模块孪生包含模块应用报告的 batteryLevel 属性。 使用此属性可以根据上次报告的电池电量水平查询和操作模块。 其他示例包括让模块应用报告模块功能或连接选项。

注意

报告的属性如何简化解决方案后端获取属性最后一个已知值的方案。 如果解决方案后端需要以带时间戳事件序列(例如时间序列)的形式处理模块遥测数据,可以使用设备到云的消息

所需属性示例

在上面的示例中,解决方案后端和模块应用使用 telemetryConfig 模块孪生的所需和报告属性来同步此模块的遥测配置。 例如:

  1. 解决方案后端使用所需配置值设置所需属性。 下面是包含所需属性集的文档的一部分:

    ...
    "desired": {
        "telemetryConfig": {
            "sendFrequency": "5m"
        },
        ...
    },
    ...
    
  2. 模块连接后,模块应用会立即收到更改通知。 如果模块未连接,则模块应用会在连接时遵循模块重新连接流。 然后,模块应用报告更新的配置(或使用 status 属性报告错误状态)。 下面是报告属性的一部分:

    "reported": {
        "telemetryConfig": {
            "sendFrequency": "5m",
            "status": "success"
        }
        ...
    }
    
  3. 解决方案后端可以通过查询模块孪生,跟踪多个模块上的配置操作结果。

注意

为便于阅读,上述代码片段示例经过优化,演示了为模块配置及其状态进行编码的一种方式。 IoT 中心不会对模块孪生中的所需属性和报告属性施加特定的架构。

后端操作

解决方案后端使用以下通过 HTTPS 公开的原子操作对模块孪生执行操作:

  • 按 ID 检索模块孪生。 此操作返回模块孪生文档,包括标记、所需的系统属性和报告的系统属性。

  • 部分更新模块孪生。 解决方案后端可以使用此操作部分更新模块孪生中的标记或所需属性。 部分更新以 JSON 文档的形式表示,可添加或更新任何属性。 将删除设置为 null 的属性。 以下示例创建值为 {"newProperty": "newValue"} 的新所需属性,将现有值 existingProperty 覆盖为 "otherNewValue",并删除 otherOldProperty。 不会对现有的所需属性或标记进行其他任何更改:

    {
        "properties": {
            "desired": {
                "newProperty": {
                    "nestedProperty": "newValue"
                },
                "existingProperty": "otherNewValue",
                "otherOldProperty": null
            }
        }
    }
    
  • 替换所需属性。 解决方案后端可以使用此操作完全覆盖所有现有的所需属性,并使用新 JSON 文档替代 properties/desired

  • 替换标记。 解决方案后端可以使用此操作完全覆盖所有现有标记,并使用新 JSON 文档替代 tags

  • 接收孪生通知。 此操作允许解决方案后端在修改孪生时收到通知。 为此,IoT 解决方案需要创建一个路由,并且将“数据源”设置为等于 twinChangeEvents。 默认情况下,不存在此类路由,因此不会发送孪生通知。 如果更改速率太高,或由于其他原因(例如内部故障),IoT 中心可能会只发送一个包含所有更改的通知。 因此,如果应用程序需要可靠地审核和记录所有中间状态,则应使用设备到云消息。 若要详细了解孪生通知消息中返回的属性和正文,请参阅非遥测事件架构

上述所有操作均支持乐观并发,并且需要控制对 IoT 中心的访问文章中定义的 ServiceConnect 权限。

除了上述操作以外,解决方案后端还可以使用类似于 SQL 的 IoT 中心查询语言查询模块孪生。

模块操作

模块应用使用以下原子操作对模块孪生执行操作:

  • 检索模块孪生。 此操作返回当前连接的模块的模块孪生文档(包括所需和报告的系统属性)。

  • 部分更新报告属性。 使用此操作可以部分更新当前连接的模块的报告属性。 此操作使用的 JSON 更新格式与解决方案后端用于部分更新所需属性的格式相同。

  • 观察所需属性。 当前连接的模块可以选择在所需属性发生更新时接收通知。 模块收到的更新格式与解决方案后端执行的更新格式相同(部分或完全替换)。

上述所有操作都需要控制对 IoT 中心的访问一文中定义的 DeviceConnect 权限。

借助 Azure IoT 设备 SDK,可通过多种语言和平台轻松使用上述操作。

标记和属性格式

标记、所需的属性和报告的属性是具有以下限制的 JSON 对象:

  • 密钥:JSON 对象中的所有键都是 UTF-8 编码、区分大小写且长度不超过 1 KB。 允许的字符不包括 UNICODE 控制字符(段 C0 和 C1)以及 .$ 和 SP。

  • :JSON 对象中的所有值可采用以下 JSON 类型:布尔值、数字、字符串、对象。 还支持数组。

    • 整数的最小值可以为 -4503599627370496,最大值可以为 4503599627370495。

    • 字符串值是 UTF-8 编码的,最大长度为 4 KB。

  • 深度:标记、所需属性和报告属性中的 JSON 对象的最大深度为 10 层。 例如,以下对象是有效的:

    {
         ...
         "tags": {
             "one": {
                 "two": {
                     "three": {
                         "four": {
                             "five": {
                                 "six": {
                                     "seven": {
                                         "eight": {
                                             "nine": {
                                                 "ten": {
                                                     "property": "value"
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         },
         ...
    }
    

模块孪生大小

IoT 中心对 tags 的值实施 8 KB 大小限制,对 properties/desiredproperties/reported 的值分别实施 32 KB 大小限制。 这些总计不包含只读元素(如 $version$metadata/$lastUpdated)。

孪生大小按如下所示计算:

  • 对于 JSON 文档中的每个属性,IoT 中心会累积计算并添加属性的键和值的长度。

  • 属性键将视为 UTF8 编码的字符串。

  • 简单属性值将视为 UTF8 编码的字符串、数值(8 字节)或布尔值(4 字节)。

  • UTF8 编码字符串的大小通过对所有字符进行计数来计算,不包括 UNICODE 控制字符(段 C0 和 C1)。

  • 复杂属性值(嵌套对象)根据它们所包含的属性键和属性值的聚合大小进行计算。

IoT 中心拒绝将这些文档的大小增加到超出限制的所有操作,在这种情况下还会返回错误。

模块孪生元数据

IoT 中心保留模块孪生所需属性和报告属性中每个 JSON 对象的上次更新时间戳。 时间戳采用 UTC,以 ISO8601 格式编码YYYY-MM-DDTHH:MM:SS.mmmZ

例如:

{
    ...
    "properties": {
        "desired": {
            "telemetryConfig": {
                "sendFrequency": "5m"
            },
            "$metadata": {
                "telemetryConfig": {
                    "sendFrequency": {
                        "$lastUpdated": "2016-03-30T16:24:48.789Z"
                    },
                    "$lastUpdated": "2016-03-30T16:24:48.789Z"
                },
                "$lastUpdated": "2016-03-30T16:24:48.789Z"
            },
            "$version": 23
        },
        "reported": {
            "telemetryConfig": {
                "sendFrequency": "5m",
                "status": "success"
            },
            "batteryLevel": "55%",
            "$metadata": {
                "telemetryConfig": {
                    "sendFrequency": "5m",
                    "status": {
                        "$lastUpdated": "2016-03-31T16:35:48.789Z"
                    },
                    "$lastUpdated": "2016-03-31T16:35:48.789Z"
                },
                "batteryLevel": {
                    "$lastUpdated": "2016-04-01T16:35:48.789Z"
                },
                "$lastUpdated": "2016-04-01T16:24:48.789Z"
            },
            "$version": 123
        }
    }
    ...
}

会在每个级别(而不仅仅是 JSON 结构的叶级别)保留此信息,以便保留删除了对象键的更新。

乐观并发

标记、所需的属性和已报告的属性都支持乐观并发。 如果需要保证孪生属性更新的顺序,请考虑通过在发送下次更新前先等待已报告的属性回调,从而在应用程序级别实现同步。

根据 etagRFC7232,模块孪生具有 ETag( 属性),它代表该孪生的 JSON 表示形式。 可以在来自解决方案后端的条件更新操作中使用 etag 属性,以确保一致性。 如果要确保涉及 tags 容器的操作中的一致性,这是唯一方案。

模块孪生所需的属性和报告的属性也包含一个保证可递增的 $version 值。 更新方可以使用类似于 ETag 的版本来强制实施更新一致性。 例如,报告的属性的模块应用,或者所需的属性的解决方案后端。

当监视代理(例如,监视所需属性的模块应用)必须协调检索操作结果与更新通知之间的资源争用时,版本也很有用。 模块重新连接流部分提供了详细信息。

模块重新连接流

IoT 中心不会保留已断开连接模块的所需属性更新通知。 它遵循的原则是:连接的模块必须检索整个所需属性文档,此外还要订阅更新通知。 如果更新通知与完全检索之间存在资源争用的可能性,则必须确保遵循以下流:

  1. 模块应用连接到 IoT 中心。
  2. 模块应用订阅所需属性更新通知。
  3. 模块应用检索所需属性的完整文档。

模块应用可以忽略 $version 小于或等于完全检索文档的版本的所有通知。 之所以能够使用此方法,是因为 IoT 中心保证版本始终是递增的。

后续步骤

要尝试本文中介绍的一些概念,请参阅以下 IoT 中心教程: