你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

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

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

本文假设读者事先已阅读了解并在 IoT 中心内使用设备孪生

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

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

注意

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

本文介绍:

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

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

模块孪生

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

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

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

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

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

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

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

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

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

设备孪生的体系结构表示形式

以下示例显示了一个模块孪生 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
        }
    }
}

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

报告属性示例

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

注意

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

所需属性示例

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

  1. 后端应用使用所需配置值设置所需属性。 下面是文档中包含所需属性集的那部分:

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

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

注意

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

重要

IoT 即插即用定义了一个架构,该架构使用几个附加属性来将更改同步到所需属性和报告属性。 如果解决方案使用 IoT 即插即用,则更新孪生体属性时必须遵循即插即用约定。 有关详细信息和示例,请参阅 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 中心查询语言查询模块孪生。

模块操作

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

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

  • 部分更新报告属性。 使用此操作可以部分更新当前连接的模块的报告属性。

  • 观察所需属性。 当前连接的模块可以选择在所需属性发生更新时接收通知。

上述所有操作都需要控制对 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)。

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

  • 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 中心教程: