

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 Amazon IoT Greengrass 将物联网数据直接摄取至 Amazon S3，经济实惠
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass"></a>

*Sebastian Viviani 和 Rizwan Syed，Amazon Web Services*

## Summary
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-summary"></a>

本文向您介绍了如何使用 Amazon IoT Greengrass Version 2 设备经济高效地将物联网 (IoT) 数据直接摄取至 Amazon Simple Storage Service (Amazon S3) 存储桶中。设备运行自定义组件，用于读取物联网数据，并将数据保存在永久存储（即本地磁盘或卷）中。然后，设备将物联网数据压缩为 Apache Parquet 文件，并定期将数据上传至 S3 存储桶。

您采集的物联网数据的数量和速度仅受边缘硬件功能以及网络带宽的限制。您可使用 Amazon Athena 经济高效地分析您摄取的数据。Athena 支持使用 [Amazon Managed Grafana](https://docs.aws.amazon.com/grafana/latest/userguide/what-is-Amazon-Managed-Service-Grafana.html) 进行压缩 Apache Parquet 文件和数据可视化。

## 先决条件和限制
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-prereqs"></a>

**先决条件**
+ 一个有效的 Amazon Web Services account
+ 在 [Amazon IoT Greengrass Version 2](https://docs.aws.amazon.com/greengrass/v2/developerguide/greengrass-v2-whats-new.html) 上运行的[边缘网关](https://docs.aws.amazon.com/greengrass/v1/developerguide/quick-start.html)手机传感器数据。（数据来源和数据收集过程超出了此模式的范围，但您几乎可以使用任何类型的传感器数据。本示例采用本地 [MQTT](https://mqtt.org/) 代理，其带传感器或网关，可在本地发布数据）。
+ Amazon IoT Greengrass [组件](https://docs.aws.amazon.com/greengrass/v2/developerguide/develop-greengrass-components.html)、[角色](https://docs.aws.amazon.com/greengrass/v1/developerguide/service-role.html)和 [SDK 依赖项](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html#installation)
+ 用于将数据上传至 S3 存储桶的[流管理器组件](https://docs.aws.amazon.com/greengrass/v2/developerguide/stream-manager-component.html)
+ [适用于 Java 的](https://aws.amazon.com/sdk-for-java/) AW [S 开发工具包、适用于 JavaScript](https://aws.amazon.com/sdk-for-javascript/) Java 的 A [WS 开发工具包或适用于 Python 的 AWS 开发工具包 (Boto3](https://docs.aws.amazon.com/pythonsdk/)) 来运行 APIs

**限制**
+ 这种模式中的数据不会实时上传至 S3 存储桶。有延迟期，您可配置延迟时间。数据在边缘设备中临时缓冲，然后在到期后上传。
+ SDK 仅可采用 Java、Node.js 和 Python 语言。

## 架构
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-architecture"></a>

**目标技术堆栈**
+ Amazon S3
+ Amazon IoT Greengrass
+ MQTT 代理
+ 流管理器组件

**目标架构**

下图显示了一种架构，该架构旨在摄取物联网传感器数据，并将该数据存储至 S3 存储桶。

![\[架构图\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/b9032ae2-fffb-4750-b161-09810e19d878/images/8c28e639-5dcf-4950-b4a6-8015ec1a2894.png)


图表显示了以下工作流：

1. 多个传感器（例如温度和阀门）更新会发布到本地 MQTT 代理。

1. 订阅这些传感器的 Parquet 文件压缩器会更新主题并接收这些更新。

1. Parquet 文件压缩器将更新项存储在本地。

1. 期限过后，存储的文件被压缩为 Parquet 文件，然后传递至流管理器，以上传到指定的 S3 存储桶。

1. 流管理器会将 Parquet 文件上传至 S3 存储桶。

**注意**  
流管理器（`StreamManager`）是托管式组件。有关如何将数据导出至 Amazon S3 的示例，请参阅 Amazon IoT Greengrass 文档中的[流管理器](https://docs.aws.amazon.com/greengrass/v2/developerguide/stream-manager-component.html)。你可以使用本地 MQTT 代理作为组件，也可以使用其他代理，比如 [Eclipse Mosquitto](https://mosquitto.org/)。

## 工具
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-tools"></a>

**AWS 工具**
+ [Amazon Athena](https://docs.aws.amazon.com/athena/latest/ug/what-is.html) 是一种交互式查询服务，可帮助您使用标准 SQL 直接在 Amazon S3 中分析数据。
+ [Amazon Simple Storage Service（Amazon S3）](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)是一项基于云的对象存储服务，可帮助您存储、保护和检索任意数量的数据。
+ [Amazon IoT Greengrass](https://docs.aws.amazon.com/greengrass/v2/developerguide/what-is-iot-greengrass.html) 是一项开源 IoT 边缘运行时和云服务，可帮助您在设备上构建、部署和管理 IoT 应用程序。

**其他工具**
+ [Apache Parquet](https://parquet.apache.org/) 是一种专为存储和检索而设计的开源列式数据文件格式。
+ [MQTT](https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html)（消息队列遥测传输）是一种轻量级消息协议，专为受限的设备而设计。

## 最佳实践
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-best-practices"></a>

**对上传的数据使用正确分区格式**

对 S3 存储桶中的根前缀名称没有具体要求（例如 `"myAwesomeDataSet/"` 或 `"dataFromSource"`），但我们建议您使用有意义的分区和前缀，以便于理解数据集的用途。

我们还建议您在 Amazon S3 中使用正确分区，以便查询在数据集上以最佳方式运行。在以下示例中，数据以 HIVE 格式分区，以便优化每个 Athena 查询扫描的数据量。这可以提高性能并降低成本。

`s3://<ingestionBucket>/<rootPrefix>/year=YY/month=MM/day=DD/HHMM_<suffix>.parquet`

## 操作说明
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-epics"></a>

### 设置您的环境
<a name="set-up-your-environment"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 S3 存储桶。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 应用程序开发人员 | 
| 添加 IAM 权限至 S3 存储桶。 | 要向用户授予您之前创建的 S3 存储桶和前缀写入权限，请将以下 IAM policy 添加至您的 Amazon IoT Greengrass 角色：<pre>{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />        {<br />            "Sid": "S3DataUpload",<br />            "Effect": "Allow",<br />            "Action": [<br />                "s3:List*",<br />                "s3:Put*"<br />            ],<br />            "Resource": [<br />                "arn:aws:s3:::<ingestionBucket>",<br />                "arn:aws:s3:::<ingestionBucket>/<prefix>/*"<br />            ]<br />        }<br />    ]<br />}</pre>有关更多信息，请参阅 Aurora 文档中的[创建 Amazon S3 资源 IAM policy](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Integrating.Authorizing.IAM.S3CreatePolicy.html)。接下来，更新 S3 存储桶的资源策略（如果需要），以允许使用正确的 AWS [主体](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-bucket-user-policy-specifying-principal-intro.html)写入。 | 应用程序开发人员 | 

### 构建和部署 Amazon IoT Greengrass 组件
<a name="build-and-deploy-the-aws-iot-greengrass-component"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 更新组件的配方。 | 当您根据以下示例[创建部署](https://docs.aws.amazon.com/greengrass/v2/developerguide/create-deployments.html)时[更新组件配置](https://docs.aws.amazon.com/greengrass/v2/developerguide/update-component-configurations.html)：<pre>{<br />  "region": "<region>",<br />  "parquet_period": <period>,<br />  "s3_bucket": "<s3Bucket>",<br />  "s3_key_prefix": "<s3prefix>"<br />}</pre>将 `<region>` 替换为您的 AWS 区域、将 `<period>` 替换为定期间隔、将 `<s3Bucket>` 替换为 S3 存储桶，将 `<s3prefix>` 替换为前缀。 | 应用程序开发人员 | 
| 创建组件。 | 请执行以下操作之一：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 应用程序开发人员 | 
| 更新 MQTT 客户端。 | 示例代码不使用身份验证，因为该组件在本地连接至代理。如果您的场景不同，请按需要更新 MQTT 客户端部分。此外，执行下列操作：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 应用程序开发人员 | 

### 将该组件添加至 Amazon IoT Greengrass Version 2 核心设备中
<a name="add-the-component-to-the-aws-iot-greengrass-version-2-core-device"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 更新核心设备部署。 | 如果 Amazon IoT Greengrass Version 2 核心设备的部署已经存在，[请修改部署](https://docs.aws.amazon.com/greengrass/v2/developerguide/revise-deployments.html)。如果部署不存在，请[创建新部署](https://docs.aws.amazon.com/greengrass/v2/developerguide/create-deployments.html)。要为组件指定正确的名称，请根据以下内容[更新新组件的日志管理器配置](https://docs.aws.amazon.com/greengrass/v2/developerguide/log-manager-component.html)（如果需要）：<pre>{<br />  "logsUploaderConfiguration": {<br />    "systemLogsConfiguration": {<br />    ...<br />    },<br />    "componentLogsConfigurationMap": {<br />      "<com.iot.ingest.parquet>": {<br />        "minimumLogLevel": "INFO",<br />        "diskSpaceLimit": "20",<br />        "diskSpaceLimitUnit": "MB",<br />        "deleteLogFileAfterCloudUpload": "false"<br />      }<br />      ...<br />    }<br />  },<br />  "periodicUploadIntervalSec": "300"<br />}</pre>最后，完成对 Amazon IoT Greengrass 核心设备部署的修订。 | 应用程序开发人员 | 

### 验证是否将数据摄取至 S3 存储桶
<a name="verify-data-ingestion-into-the-s3-bucket"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 查看 Amazon IoT Greengrass 卷日志。 | 检查以下各项：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 应用程序开发人员 | 
| 检查 S3 存储桶。 | 验证数据是否正在上传至 S3 存储桶。您可以看到每个时间段在上传的文件。您还可以通过查询下一部分中的数据，验证数据是否已上传至 S3 存储桶。 | 应用程序开发人员 | 

### 设置来自 Athena 的查询
<a name="set-up-querying-from-athena"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建数据库和表。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 应用程序开发人员 | 
| 授予 Athena 数据访问权限。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 应用程序开发人员 | 

## 问题排查
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-troubleshooting"></a>


| 问题 | 解决方案 | 
| --- | --- | 
| MQTT 客户端无法连接 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 
| MQTT 客户端订阅失败 | 验证 MQTT 代理权限。如果你有来自 AWS 的 MQTT 代理，请参阅 [MQTT 3.1.1 代理 (Moquette)](https://docs.aws.amazon.com/greengrass/v2/developerguide/mqtt-broker-moquette-component.html) 和 [MQTT 5 代理 (EMQX)](https://docs.aws.amazon.com/greengrass/v2/developerguide/mqtt-broker-emqx-component.html)。 | 
| 无法创建 Parquet 文件 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 
| 对象未上传至 S3 存储桶 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass.html) | 

## 相关资源
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-resources"></a>
+ [DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html)（Pandas 文档）
+ [Apache Parquet 文档](https://parquet.apache.org/docs/)（Parquet 文档）
+ [开发 Amazon IoT Greengrass 组件](https://docs.aws.amazon.com/greengrass/v2/developerguide/develop-greengrass-components.html)（Amazon IoT Greengrass 开发人员指南，第 2 版）
+ [将 Amazon IoT Greengrass 组件部署至设备](https://docs.aws.amazon.com/greengrass/v2/developerguide/manage-deployments.html)（Amazon IoT Greengrass 开发人员指南，第 2 版）
+ [与本地物联网设备互动](https://docs.aws.amazon.com/greengrass/v2/developerguide/interact-with-local-iot-devices.html)（Amazon IoT Greengrass 开发人员指南，第 2 版）
+ [MQTT 3.1.1 代理 (Moquette)](https://docs.aws.amazon.com/greengrass/v2/developerguide/mqtt-broker-moquette-component.html)（Amazon IoT Greengrass 开发人员指南，第 2 版）
+ [MQTT 5 代理 (EMQX)](https://docs.aws.amazon.com/greengrass/v2/developerguide/mqtt-broker-emqx-component.html)（Amazon IoT Greengrass 开发人员指南，第 2 版）

## 附加信息
<a name="cost-effectively-ingest-iot-data-directly-into-amazon-s3-using-aws-iot-greengrass-additional"></a>

**成本分析**

以下成本分析场景演示了此模式中涵盖的数据摄取方法如何影响 Amazon Web Services Cloud 中的数据摄取成本。此场景中的定价示例基于发布价格。价格可能会发生变化。此外，您的费用可能会有所不同，具体取决于您的 AWS 区域、AWS 服务限额以及与云环境相关的其他因素。

*输入信号集*

该分析使用以下一组输入信号为基础，将物联网摄取成本与其他可用替代方案进行比较。


| 
| 
| 信号数量 | 频率 | 每个信号的数据 | 
| --- |--- |--- |
| 125 | 25 Hz | 8 字节 | 

在此情况下，系统接收 125 个信号。每个信号为 8 字节，每 40 毫秒 (25 Hz) 会出现一次。这些信号可单独发出，也可以分组至公共有效载荷中。您可根据需要选择拆分和打包这些信号。您还可确定延迟。延迟由接收、累积和摄取数据时间段组成。

为了便于比较，此场景的摄取操作基于 `us-east-1` AWS 区域。成本比较仅适用 Amazon Web Services。硬件或连接等其他成本未计入分析。

*成本比较*

下表显示了每种摄取方法的每月费用（以美元为单位）。


| 
| 
| 方法 | 月度成本 | 
| --- |--- |
| AWS 物联网 SiteWise *\$1* | 331.77 美元 | 
| 带有数据处理包的 AWS IoT SiteWise Edge（将所有数据保存在边缘） | 200 美元 | 
| AWS IoT Core 和 Amazon S3 访问原始数据规则 | 84.54 美元 | 
| 边缘 Parquet 文件压缩并上传至 Amazon S3 | 0.5 美元 | 

\$1必须对数据进行缩减采样，才能符合服务限额。这意味着使用此方法会丢失数据。

*替代方法*

本节显示了以下替代方法等效成本：
+ **AWS IoT SiteWise** — 每个信号都必须以单独的消息形式上传。因此，每月的消息总数为 125×25×3600×24×30，相当于每月 81 亿条消息。但是，AWS IoT 每个属性每秒 SiteWise 只能处理 10 个数据点。假设将数据缩减到 10 Hz，则每月的消息数量将减少到125×10×3600×24×30，即 32.4 亿条。如果您使用发布者组件，该组件以 10 个为一组（每百万封邮件 1 美元）打包，则每月的费用为每月 324 美元。假设每条消息为 8 字节 (1 Kb/125)，则为 25.92 Gb 的数据存储空间。这增加了每月 7.77 美元的费用。第一个月的总费用为 331.77 美元，每月增加 7.77 美元。
+ **带有数据处理包的 AWS IoT SiteWise Edge，包括在边缘完全处理的所有模型和信号（即无需云端接入）**— 您可以使用数据处理包作为替代方案，以降低成本并配置在边缘计算的所有模型。即使没有进行实际计算，也可以仅用于存储和可视化。在这种情况下，必须为边缘网关使用强大硬件。每月的固定费用为 200 美元。
+ **MQTT 直接接入 AWS IoT Core by MQTT 以及物联网规则，以将原数据存储至 Amazon S3** — 假设所有信号都发布在公共负载中，则发布到 AWS IoT Core 的消息总数为 25×3600×24×30，即每月 6,480 万条。按每百万条消息 1 美元算，每月费用为 64.8 美元。按每百万条规则激活 0.15 美元，每条消息一条规则，每月增加 19.44 美元的费用。按照 Amazon S3 中每 Gb 存储空间 0.023 美元成本，每月再增加 1.5 美元（每月增加以反映新数据）。第一个月的总费用为 84.54 美元，每月增加 1.5 美元。
+ **在边缘压缩 Parquet 文件中的数据并上传至 Amazon S3（建议的方法**）— 压缩率取决于数据的类型。使用针对 MQTT 测试的相同工业数据，整个月的总产出数据为 1.2 Gb。每月的费用为 0.03 美元。其他基准测试中描述的压缩率（使用随机数据）约为 66％（更接近最坏的情况）。总数据量为 21 Gb，每月花费 0.5 美元。

**Parquet 文件生成器**

以下代码示例显示了用 Python 编写的 Parquet 文件生成器结构。该代码示例仅用于说明，如果粘贴到您的环境中则不起作用。

```
import queue
import paho.mqtt.client as mqtt
import pandas as pd

#queue for decoupling the MQTT thread
messageQueue = queue.Queue()
client = mqtt.Client()
streammanager = StreamManagerClient()

def feederListener(topic, message):
    payload = {
        "topic" : topic,
        "payload" : message,
    }
    messageQueue.put_nowait(payload)

def on_connect(client_instance, userdata, flags, rc):
    client.subscribe("#",qos=0)

def on_message(client, userdata, message):
    feederListener(topic=str(message.topic), message=str(message.payload.decode("utf-8")))


filename = "tempfile.parquet"
streamname = "mystream"
destination_bucket= "amzn-s3-demo-bucket"
keyname="mykey"
period= 60

client.on_connect = on_connect
client.on_message = on_message
streammanager.create_message_stream(
            MessageStreamDefinition(name=streamname, strategy_on_full=StrategyOnFull.OverwriteOldestData)
        )


while True:
   try:
       message = messageQueue.get(timeout=myArgs.mqtt_timeout)
   except (queue.Empty):
       logger.warning("MQTT message reception timed out")

   currentTimestamp = getCurrentTime()
   if currentTimestamp >= nextUploadTimestamp:
       df = pd.DataFrame.from_dict(accumulator)
       df.to_parquet(filename)
       s3_export_task_definition = S3ExportTaskDefinition(input_url=filename, bucket=destination_bucket, key=key_name)
       streammanager.append_message(streamname, Util.validate_and_serialize_to_json_bytes(s3_export_task_definition))
       accumulator = {}
       nextUploadTimestamp += period
   else:
        accumulator.append(message)
```