IoT-For-Beginners 第 9 课:用 Azure Functions 无服务器架构将农田灌溉继电器控制逻辑迁移上云
2026/9/16 18:18:13 网站建设 项目流程

IoT-For-Beginners 第 9 课:用 Azure Functions 无服务器架构将农田灌溉继电器控制逻辑迁移上云

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

本篇指南基于 IoT-For-Beginners 课程中“Migrate your application logic to the cloud”一课(丹麦语版本位于 translations/da/2-farm/lessons/5-migrate-application-to-the-cloud/README.md,英文原文位于 2-farm/lessons/5-migrate-application-to-the-cloud/README.md)。在前一课中,你已经把植物的土壤湿度监控与继电器(水泵)控制接入了云端的 Azure IoT Hub;本课的核心任务是把“决定继电器何时开/关”的服务器端逻辑从自托管的 MQTT 监听程序迁移到云端的无服务器函数(Serverless Functions)中。读完后,你将掌握:Azure Functions 项目初始化、通过 Event Hub 兼容端点消费 IoT Hub 遥测消息、用 Registry Manager 向设备下发 Direct Method 指令,以及把整套代码部署为云端 Function App 的完整操作流程。

一、什么是无服务器计算?

无服务器(Serverless)计算的本质是:编写小的代码块,在云端响应各类事件而运行。事件发生时,你的代码被加载并执行,同时收到关于该事件的数据。事件可以来自多种来源:Web 请求、队列中的消息、数据库变更,或是 IoT 设备发送到 IoT 服务的遥测消息。

如果你对数据库触发器(database trigger)不陌生,可以把它类比为同一件事:由“插入一行”之类的事件触发执行的代码。

无服务器的关键特性与代价:

  • 仅在事件发生时运行:没有任何进程在事件之外“保活”你的代码。事件到来 → 代码加载 → 执行 → 结束。这使得扩展性极佳:当大量事件同时到达时,云厂商可以在可用服务器上并行运行任意多次你的函数实例;
  • 无内存状态:需要在多次事件之间共享信息时,必须把状态存入数据库等外部存储,而不能依赖进程内存;
  • 以函数形式编写:你的代码是一个函数,以事件详情作为参数。Azure Functions 开箱即用地支持 Python、JavaScript、TypeScript、C#、F#、Java 和 PowerShell;也支持自定义处理器(custom handlers),因此理论上可以用任何能处理 HTTP 请求的语言(甚至 COBOL 这样的老语言)编写函数;
  • 名字里没有“无服务器”这件事其实是成立的:无服务器背后仍有服务器,只是作为开发者你不再关心服务器本身。云厂商的无服务器runtime负责分配服务器、网络、存储、CPU、内存等一切资源。因此计费不按“每台服务器”,而是按代码运行时间 × 使用的内存量收取。有云厂商提供每月合计 100 万次执行免费额度,超出后约每 100 万次 0.20 美元——代码不运行就不产生费用;
  • FaaS:无服务器也称为 Functions as a Service,因为每个事件触发器都以代码中的一个函数实现。

对 IoT 开发者而言,这个模型非常契合:你编写一个函数,它对连接到云端 IoT 服务的任意IoT 设备发来的消息做出响应——代码能处理所有消息,却只在需要时运行。

Functions app 的结构:一个 Functions app 由一个或多个triggers(触发器)组成,每个 trigger 就是一个响应事件的函数。一个 app 内可以有多个 trigger,它们共享公共配置——例如在 app 的配置文件中放 IoT Hub 的连接信息,app 内所有函数都能用它来连接并监听事件。

二、创建无服务器应用:安装工具链

微软的无服务器计算服务叫Azure Functions。写 Azure Functions 的第一步是准备本地开发环境。

适用前提提醒:本课程写作时,Azure Functions 工具链在 Apple Silicon 芯片 Mac 上对 Python 项目尚不完全可用,需使用 Intel Mac、Windows PC 或 Linux PC。

无服务器的一个重大优势是本地可运行:与云端相同的 runtime 可以跑在本地计算机上,你能编写响应 IoT 消息的代码并在本地运行、甚至调试,确认无误后再部署上云。

任务 1:安装 Azure Functions 工具

Azure Functions 工具是一个 CLI,称为Azure Functions Core Tools

  1. 按照官方“Azure Functions Core Tools”文档安装核心工具;
  2. 在 VS Code 中安装Azure Functions 扩展,它提供创建、调试和部署 Azure Functions 的支持。

部署时,Functions app 需要少量云存储来保存应用文件和日志;本地运行时同样需要连接存储,但可以用本地存储模拟器Azurite替代真实的云存储。Azurite 是一个 Node.js 应用,在本地运行但表现得像云存储。

补充一个 Azure 背景知识:Azure Functions 使用的存储是Azure Storage Account,一个账户可以同时存放文件、blob、表数据和队列数据,并且可以在多个 app(例如一个 Functions app 和一个 Web app)之间共享。

Azurite 的安装与启动步骤:

  1. 由于 Azurite 是 Node.js 应用,先安装 Node.js(macOS 上也可通过 Homebrew 安装);

  2. 安装 Azurite(npm随 Node.js 一起安装):

    npm install -g azurite
  3. 创建 Azurite 存放数据的目录:

    mkdir azurite
  4. 启动 Azurite 并指向该目录:

    azurite --location azurite

Azurite 启动后输出如下,表示本地 Functions runtime 可以连接:

➜ ~ azurite --location azurite Azurite Blob service is starting at http://127.0.0.1:10000 Azurite Blob service is successfully listening at http://127.0.0.1:10000 Azurite Queue service is starting at http://127.0.0.1:10001 Azurite Queue service is successfully listening at http://127.0.0.1:10001 Azurite Table service is starting at http://127.0.0.1:10002 Azurite Table service is successfully listening at http://127.0.0.1:10002

任务 2:创建 Azure Functions 项目

Azure Functions CLI 用于创建新的 Functions app。

  1. 创建并进入 Functions app 目录,命名为soil-moisture-trigger

    mkdir soil-moisture-trigger cd soil-moisture-trigger
  2. 在该目录下创建 Python 虚拟环境:

    python3 -m venv .venv
  3. 激活虚拟环境:

    • Windows Command Prompt:.venv\Scripts\activate.bat
    • Windows PowerShell:.\.venv\Scripts\Activate.ps1
    • macOS / Linux:source ./.venv/bin/activate

    这些命令必须在创建虚拟环境时所在的目录中执行;永远不需要cd进入.venv目录,所有安装依赖、运行代码的命令都从创建虚拟环境的那个目录执行。

  4. 初始化 Functions app:

    func init --worker-runtime python soil-moisture-trigger

    这会在当前目录生成三个文件(仓库中的参考实现同样具备这三个文件,见 code/functions/soil-moisture-trigger):

    • host.json—— Functions app 级别的设置 JSON 文档,一般无需修改。参考仓库中的 host.json 可以看到它包含日志采样配置(Application Insights,excludedTypes: Request)以及扩展程序包声明:

      { "version": "2.0", "logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" } } }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[2.*, 3.0.0)" } }

      从源码结构看,extensionBundle[2.*, 3.0.0)版本约束与az functionapp create --functions-version 3创建 v3 运行时相呼应,用于让平台自动加载匹配版本的触发器扩展包。

    • local.settings.json—— 本地运行时使用的设置,例如 IoT Hub 连接字符串。仅本地使用,不应提交到版本控制;部署到云端后这些设置不会随 app 一起部署,而是由 Application Settings 提供(下文部署章节详述)。参考仓库中的 local.settings.json:

      { "IsEncrypted": false, "Values": { "FUNCTIONS_WORKER_RUNTIME": "python", "AzureWebJobsStorage": "UseDevelopmentStorage=true", "IOT_HUB_CONNECTION_STRING": "<connection string>", "REGISTRY_MANAGER_CONNECTION_STRING": "<connection string>" } }
    • requirements.txt—— Pip 依赖清单文件,声明运行 Functions app 所需的包。仓库参考实现中的 requirements.txt 内容:

      # Do not include azure-functions-worker as it may conflict with the Azure Functions platform azure-functions azure-iot-hub

      注意这条注释:不要手动加入azure-functions-worker,因为它由平台提供,自行声明可能冲突。依赖必须写进此文件,部署到云端时 runtime 才能确保安装正确的包。

  5. local.settings.json中有一个 Functions app 使用的存储账户设置,默认为空,需要填入以连接 Azurite 模拟器:

    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
  6. 安装 Pip 依赖:

    pip install -r requirements.txt
  7. 启动 Functions runtime 做验证:

    func start

    runtime 启动后会报告未发现任何 job function(trigger):

    (.venv) ➜ soil-moisture-trigger func start Found Python version 3.9.1 (python3). Azure Functions Core Tools Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit) Function Runtime Version: 3.0.15417.0 [2021-05-05T01:24:46.795Z] No job functions found.

    ⚠️ 若弹出防火墙提示,请允许func应用访问网络(它需要读写网络);若在 macOS 上看到/dev/shm/AzureFunctions共享内存目录的告警,只要 app 能正常启动并列出函数即可忽略。

  8. ctrl+c停止 app。

  9. 用 VS Code 打开当前目录(可直接执行code .)。VS Code 会检测出这是一个 Azure Functions 项目并弹出提示 “Detected an Azure Functions Project in folder "soil-moisture-trigger" ... Initialize for optimal use with VS Code?”,选择Yes

  10. 确保 VS Code 终端中运行的是已激活的 Python 虚拟环境,必要时先结束并重启终端。

三、创建 IoT Hub 事件触发器

Functions app 只是无服务器代码的“壳”。要响应 IoT Hub 事件,需要给 app 加一个 trigger:它连接到发送进 IoT Hub 的消息流并做出响应。获取这条消息流的方式,是让 trigger 连接 IoT Hub 的event hub 兼容端点(event hub compatible endpoint)。

原理背景:IoT Hub 构建在另一个 Azure 服务Azure Event Hubs之上。Event Hubs 提供消息的发送与接收能力,IoT Hub 在此之上扩展了面向 IoT 设备的功能。因此读取 IoT Hub 消息的连接方式与使用 Event Hubs 完全相同——Azure Functions 没有专门的 “IoT Hub trigger”,用的就是Event Hub trigger

关于安全:IoT 设备连接 IoT Hub 必须使用密钥,保证只有被允许的设备能接入;以读取消息的身份连接同理——你的代码需要一个包含密钥和 IoT Hub 信息的连接字符串。

注意:默认拿到的连接字符串带有iothubowner权限,任何使用它的代码都对 IoT Hub 拥有完全权限。理想情况下应使用所需权限的最低级别(本仓库后续的安全课程专门处理该问题)。

trigger 连接成功后,函数内的代码会对发往 IoT Hub 的每一条消息被调用(无论来自哪台设备),消息作为参数传入。

任务 1:获取 event hub 兼容端点的连接字符串

  1. 在 VS Code 终端执行(将<hub_name>替换为你的 IoT Hub 名称):

    az iot hub connection-string show --default-eventhub \ --output table \ --hub-name <hub_name>
  2. 打开local.settings.json,在Values节添加:

    "IOT_HUB_CONNECTION_STRING": "<connection string>"

    用上一步的值替换<connection string>,并在该行后补逗号以保持 JSON 合法。

任务 2:创建事件触发器

soil-moisture-trigger目录内执行:

func new --name iot-hub-trigger --template "Azure Event Hub trigger"

这会生成名为iot-hub-trigger的新函数,并在soil-moisture-trigger下创建同名目录,内含两个文件:

__init__.py—— 包含 trigger 的 Python 代码文件,利用 Python 的包命名约定把该目录变成一个模块。模板生成的初始代码:

import logging import azure.functions as func def main(event: func.EventHubEvent): logging.info('Python EventHub trigger processed an event: %s', event.get_body().decode('utf-8'))

trigger 的核心是main函数——它会被 IoT Hub 事件调用。参数event是一个EventHubEvent:每当有消息发到 IoT Hub,该函数就被调用一次,消息作为event传入,同时携带与前一课看到的 MQTT 消息标注(annotations)相同的属性。初始代码的核心就是打印一条日志。

function.json—— trigger 的配置文件,主体是bindings节。Binding(绑定)是 Azure Functions 与其他 Azure 服务之间连接关系的术语。该函数有一个指向 event hub 的输入绑定:连接到 event hub 并接收数据。当然也可以有输出绑定——例如加一个到数据库的输出绑定后,把 IoT Hub 事件从函数返回,就会被自动插入数据库。

bindings中值得关注的值:

值(模板默认)含义
typeeventHubTrigger告诉函数监听 Event Hub 事件
namemain函数参数名一致Event Hub 事件传入函数时使用的参数名,必须与 Python 代码main(event)的参数名匹配
directionin输入绑定:数据从 event hub 流入函数
connection设置项名称声明从哪个“设置”读取连接字符串;本地运行时从local.settings.json读取

安全设计:连接字符串不能直接写在function.json里,只能通过设置项间接引用——防止你不小心把密钥暴露进配置文件。

对照仓库中的参考实现 function.json,修正完成后的完整内容如下:

{ "scriptFile": "__init__.py", "bindings": [ { "type": "eventHubTrigger", "name": "event", "direction": "in", "eventHubName": "", "connection": "IOT_HUB_CONNECTION_STRING", "cardinality": "one", "consumerGroup": "$Default", "dataType": "binary" } ] }

可以观察到,模板生成的cardinalityeventHubName需要按下面三步修正,而consumerGroup: "$Default"dataType: "binary"保持模板默认即可(consumerGroup即文档提示的“消费者组”机制,多个 app 可用不同 consumer group 同时消费同一端点,仓库后续课程有专门讲解)。

修正步骤:

  1. 由于 Azure Functions 模板存在已知缺陷(模板仓库 issue #1250),function.jsoncardinality字段默认值错误,需从many改为:

    "cardinality": "one",
  2. "connection"更新为指向你在local.settings.json中添加的设置项名称:

    "connection": "IOT_HUB_CONNECTION_STRING",

    记住:这里填的是设置项名称,不是连接字符串本身。

  3. 由于连接字符串中已包含eventHubName值,function.json里对应字段需清空:

    "eventHubName": "",

任务 3:运行事件触发器

  1. 确认没有同时运行 IoT Hub 事件监控程序(前一课的服务器代码)。若两者并行,Functions app 无法连接并消费事件。

  2. 运行 Functions app:

    func start

    app 启动后发现有iot-hub-trigger函数,随后会处理最近一天内已发送到 IoT Hub 的事件:

    (.venv) ➜ soil-moisture-trigger func start Found Python version 3.9.1 (python3). Azure Functions Core Tools Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit) Function Runtime Version: 3.0.15417.0 Functions: iot-hub-trigger: eventHubTrigger For detailed output, run func with --verbose flag. [2021-05-05T02:44:07.517Z] Worker process started and initialized. [2021-05-05T02:44:09.202Z] Executing 'Functions.iot-hub-trigger' (Reason='(null)', Id=802803a5-eae9-4401-a1f4-176631456ce4) [2021-05-05T02:44:09.205Z] Trigger Details: PartitionId: 0, Offset: 1011240-1011632, EnqueueTimeUtc: 2021-05-04T19:04:04.2030000Z-2021-05-04T19:04:04.3900000Z, SequenceNumber: 2546-2547, Count: 2 [2021-05-05T02:44:09.352Z] Python EventHub trigger processed an event: {"soil_moisture":628} [2021-05-05T02:44:09.354Z] Python EventHub trigger processed an event: {"soil_moisture":624} [2021-05-05T02:44:09.395Z] Executed 'Functions.iot-hub-trigger' (Succeeded, Id=802803a5-eae9-4401-a1f4-176631456ce4, Duration=245ms)

    每次函数调用都被Executing 'Functions.iot-hub-trigger'/Executed 'Functions.iot-hub-trigger'日志块包围,从中可以看到每次调用处理了多少条消息(上例为一次调用内 2 条)。Trigger Details行暴露了底层 Event Hubs 的消费细节:分区号(PartitionId)、偏移量(Offset)、入队时间、序列号——这正是 Event Hub 兼容端点按 Event Hubs 语义工作的直接证据。

  3. 确保 IoT 设备在运行,你会看到新的土壤湿度消息持续出现在 Functions app 输出中。

  4. 停止并重启 Functions app:它不会重复处理旧消息,只处理新消息(消费偏移被持久化,本地由 Azurite 存储,云端由 Storage Account 存储——这就是本地也需要“存储”的原因)。

VS Code 还支持直接调试函数:点击行首边缘、Run -> Toggle breakpoint或按F9设断点;按F5Run -> Start debugging或运行调试面板的Start debugging按钮启动调试器,即可查看正在处理的事件详情。

故障排查
  • 若报错:

    The listener for function 'Functions.iot-hub-trigger' was unable to start. Microsoft.WindowsAzure.Storage: Connection refused. System.Net.Http: Connection refused. System.Private.CoreLib: Connection refused.

    检查 Azurite 是否在运行,以及local.settings.jsonAzureWebJobsStorage是否设为UseDevelopmentStorage=true

  • 若报错:

    System.Private.CoreLib: Exception while executing function: Functions.iot-hub-trigger. System.Private.CoreLib: Result: Failure Exception: AttributeError: 'list' object has no attribute 'get_body'

    检查function.jsoncardinality是否为one。该错误的机理:cardinalitymany时,触发器把一批事件作为列表传入,event.get_body()自然不存在。

  • 若报错:

    Azure.Messaging.EventHubs: The path to an Event Hub may be specified as part of the connection string or as a separate value, but not both. Please verify that your connection string does not have the `EntityPath` token if you are passing an explicit Event Hub name. (Parameter 'connectionString').

    检查function.jsoneventHubName是否已设为空字符串——事件 Hub 路径要么由连接字符串携带,要么由eventHubName单独指定,二者只能选其一。

四、从服务器代码发送 Direct Method 请求

到目前为止,Functions app 只是通过 event hub 兼容端点监听消息。下一步是要向 IoT 设备发送命令,这需要经Registry Manager建立另一条到 IoT Hub 的连接。Registry Manager 可以:查看已注册设备、发送 cloud-to-device 消息、发送 Direct Method 请求、更新设备孪生,以及注册、更新、删除设备。

任务 1:获取 Registry Manager 连接字符串

  1. 执行(<hub_name>替换为实际 Hub 名):

    az iot hub connection-string show --policy-name service \ --output table \ --hub-name <hub_name>

    这里用--policy-name service请求的是ServiceConnect策略的连接字符串。申请连接字符串时可以选择其权限级别:ServiceConnect 策略允许代码连接并向 IoT 设备发送消息——比 iothubowner 权限低得多,体现了“最低权限”实践。

  2. local.settings.jsonValues节添加:

    "REGISTRY_MANAGER_CONNECTION_STRING": "<connection string>"

任务 2:向设备发送 Direct Method 请求

  1. Registry Manager 的 SDK 以 Pip 包形式提供,在requirements.txt中加入(见参考实现,该文件最终为azure-functions+azure-iot-hub两行):

    azure-iot-hub
  2. 确保 VS Code 终端已激活虚拟环境,安装依赖:

    pip install -r requirements.txt
  3. __init__.py顶部添加导入:

    import json import os from azure.iot.hub import IoTHubRegistryManager from azure.iot.hub.models import CloudToDeviceMethod
  4. 清空main方法体(保留方法本身),依次加入以下代码。

    第一步——解析事件,提取消息体与设备 ID:

    body = json.loads(event.get_body().decode('utf-8')) device_id = event.iothub_metadata['connection-device-id'] logging.info(f'Received message: {body} from {device_id}')

    event.get_body()是设备发来的遥测 JSON(如{"soil_moisture":628});iothub_metadata字典由 IoT Hub 自动附加,包含发送者设备 ID、消息时间等属性。

    第二步——根据湿度阈值构造 Direct Method 请求:

    soil_moisture = body['soil_moisture'] if soil_moisture > 450: direct_method = CloudToDeviceMethod(method_name='relay_on', payload='{}') else: direct_method = CloudToDeviceMethod(method_name='relay_off', payload='{}')

    代码读取遥测中的soil_moisture值:超过阈值 450 时构造relay_on请求,否则构造relay_off。方法请求本身不需要有效载荷,因此发送空 JSON 文档'{}'。这两个方法名正对应前几课中设备端 Arduino 固件注册的可调用方法。

    第三步——从环境读取连接字符串并创建 Registry Manager 实例:

    logging.info(f'Sending direct method request for {direct_method.method_name} for device {device_id}') registry_manager_connection_string = os.environ['REGISTRY_MANAGER_CONNECTION_STRING'] registry_manager = IoTHubRegistryManager(registry_manager_connection_string)

    local.settings.json中的值会被本地 runtime 注入为环境变量,通过os.environ(返回所有环境变量的字典)读取。

    部署到云端后,这些值将以Application Settings的形式存在,同样可从环境变量读取——本地与云端的代码完全一致,这正是配置外部化设计的好处。

    第四步——向“发送这条遥测的那台设备”下发指令:

    registry_manager.invoke_device_method(device_id, direct_method) logging.info('Direct method request sent!')

    完整实现见仓库参考文件 iot-hub-trigger/init.py,与上述分段拼合后完全一致。

    与早期 MQTT 版本的关键差异:此前用 MQTT 主题控制继电器时,控制命令会广播给所有设备(代码假设只有一台设备)。现在 Direct Method 请求被定向到单一设备device_id来自事件的connection-device-id),因此即使部署多套“湿度传感器 + 继电器”装置,每条指令也只发给正确的设备。

  5. 运行 Functions app,确保 IoT 设备在发送数据:终端会显示消息被处理、Direct Method 请求被发出;把土壤湿度传感器反复插入/拔出土壤,可以看到数值变化、继电器随之通断。

五、把无服务器代码部署到云端

代码已在本地跑通,下一步是把 Functions App 部署上云。

任务 1:创建云资源

Functions App 需部署到 Azure 中的 Function App 资源(放在为 IoT Hub 创建的资源组里),同时需要一个真正的 Storage Account 替代本地的 Azurite 模拟器。

  1. 创建存储账户:

    az storage account create --resource-group soil-moisture-sensor \ --sku Standard_LRS \ --name <storage_name>
    • <storage_name>必须全局唯一(它是访问 URL 的一部分),只能用小写字母和数字,最长 24 字符。建议用sms之类的前缀加一个唯一后缀(随机词或你的名字);
    • --sku Standard_LRS选择最低价的标准通用层(本地冗余)。存储没有免费层,按用量付费,成本相对很低(最贵的存储类型也低于每月每 GB 0.05 美元)。
  2. 创建 Function App:

    az functionapp create --resource-group soil-moisture-sensor \ --runtime python \ --functions-version 3 \ --os-type Linux \ --consumption-plan-location <location> \ --storage-account <storage_name> \ --name <functions_app_name>

    各参数说明:

    • <location>:创建资源组时使用的同一位置;
    • <storage_name>:上一步创建的存储账户名;
    • <functions_app_name>:全局唯一的函数应用名(也是 URL 的一部分),建议soil-moisture-sensor-前缀加唯一后缀;
    • --functions-version 3:选用 Azure Functions 第 3 版运行时(本课程写作时的最新版,与本地host.json中扩展包版本约束[2.*, 3.0.0)兼容);
    • --os-type Linux:Functions 可托管在 Linux 或 Windows 上,视语言而定——Python 应用仅支持 Linux
    • --consumption-plan-location:采用 Consumption(按量)计费计划,与该课“按执行次数和运行时长计费”的无服务器理念一致。

任务 2:上传应用设置

开发期间,IoT Hub 连接字符串存在local.settings.json中;上云前必须把它们写入 Function App 的Application Settings,代码才能取到。

概念对照:local.settings.json仅用于本地开发,不应提交到 Git 等源码控制;云端使用 Application Settings——一组托管在云端的键/值对,由 runtime 或你的代码以环境变量的形式读取。本地与云端由此实现“同一份代码、不同配置来源”。

  1. 设置IOT_HUB_CONNECTION_STRING

    az functionapp config appsettings set --resource-group soil-moisture-sensor \ --name <functions_app_name> \ --settings "IOT_HUB_CONNECTION_STRING=<connection string>"

    其中<connection string>local.settings.json中同名键的值。

  2. 重复上一步,把REGISTRY_MANAGER_CONNECTION_STRING设为local.settings.json中的对应值。

执行这些命令时,Azure CLI 还会回显该 Function App 的全部Application Settings,可借此核对值是否正确。注意你会看到AzureWebJobsStorage已有值:本地它指向 Azurite 模拟器,而云端在创建 Function App 时传入--storage-account参数后,平台已自动把它写好了。

任务 3:发布 Functions App

func azure functionapp publish <functions_app_name>

代码会被打包并发送到 Function App,在那里部署并启动。控制台输出量较大,结尾是部署确认与已部署函数清单——本例只有那个 trigger:

Deployment successful. Remote build succeeded! Syncing triggers... Functions in soil-moisture-sensor: iot-hub-trigger - [eventHubTrigger]

确认 IoT 设备在运行:通过调节土壤湿度或把传感器插入/拔出土壤来改变湿度水平,继电器会随之自动通断。至此,控制继电器时机的“服务器代码”已完全从你的电脑移到了云端:设备上报遥测 → Event Hub 兼容端点 → Azure Functions 触发 → Registry Manager 下发 Direct Method → 设备执行。

六、思考题与课后作业

思考挑战:在前一课中,你用“继电器开启期间退订 MQTT 消息、关闭后短暂继续退订”的手法来控制继电器通断节奏。这套办法在无服务器场景不可用——你无法让 IoT Hub trigger “退订”。请思考:在 Functions App 中可以用哪些方式处理同样的时序控制需求?(提示方向:利用iothub_metadata中的时间戳做状态判断、引入外部存储记录上次操作时间等,均需借助前文所述“无服务器没有内存状态”的特性来设计。)

课后作业——添加手动继电器控制(详见 assignment.md):无服务器代码可以被很多事物触发,HTTP 请求即是其一。作业要求给 Functions App 添加两个 HTTP trigger(relay_on/relay_off),复用本课学到的 Direct Method 发送逻辑,实现通过 Web 请求手动开关继电器。作业给出的关键提示:

  • 创建 HTTP trigger:func new --name <trigger name> --template "HTTP trigger"
  • HTTP trigger 默认需要函数专属 API 键访问控制;若想让任何人可调用,把该 trigger 的function.jsonauthLevel改为"anonymous"(生产环境不建议这样做);
  • HTTP trigger 默认同时支持 GET 与 POST,本地运行时可在终端看到 URL(形如http://localhost:7071/api/relay_on),直接用浏览器打开即可触发;
  • 部署后 URL 形如https://<functions app name>.azurewebsites.net/api/<trigger name>,注意 HTTP trigger 默认挂在api子路径下。

七、小结:本课建立的云侧控制链路

环节机制关键配置/代码
事件来源IoT 设备遥测进入 IoT Hub设备侧沿用前一课的 MQTT 上报
事件消费Event Hub 兼容端点 + Event Hub triggerfunction.jsoneventHubTrigger绑定、cardinality: oneeventHubName: ""
本地存储Azurite 模拟 Storage AccountAzureWebJobsStorage: UseDevelopmentStorage=true
指令下发Registry Manager + Direct Methodazure-iot-hub包、invoke_device_method(device_id, ...)
凭据管理连接字符串只存设置项,经环境变量读取local.settings.json→ 云端 Application Settings
部署func azure functionapp publishConsumption 计划、Python 3 运行时、Linux

参考实现的完整代码可在 code/functions 目录逐文件对照;英文原版课程文档(含更多研究链接与视频)见 2-farm/lessons/5-migrate-application-to-the-cloud/README.md,后续课程(存储位置数据、安全加固等)可参考 docs/_sidebar.md 中的课程目录继续学习。

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询