尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

无线网关接入Azure IoT Stack实战:架构、选型与排坑指南

无线网关接入Azure IoT Stack实战:架构、选型与排坑指南 做物联网项目这几年我有个特别深的体会设备上云这件事最折腾人的往往不是云平台本身而是“设备怎么稳定地把数据送上来”。尤其当你手里是一堆跑着LoRa、Zigbee、BLE的传感器节点想让它们走进Azure IoT这套技术栈时中间一定需要一个能“翻译语言、统一出口”的角色这就是无线网关。今天这篇就围绕“无线网关接入Azure-Ready IoT Stack”这个项目把架构思路、硬件选型、云上对接、实操过程和排坑经验一次性摊开讲清楚。无论你是正在做网关产品选型还是想把现有无线传感网络搬到Azure上这篇文章都值得你花十分钟认真看看。1. 整体架构思路为什么是无线网关这组云技术栈搭配1.1 无线网关在整个物联网链路里的真实位置很多刚开始接触物联网的人会有一个误解以为传感器直接连Wi-Fi、直接调云平台API就算“设备上云”了。但实际到了生产环境你会发现事情远没那么简单。现场的设备往往是电池供电的功耗受限不能一直开着Wi-Fi收发设备安装位置可能在地下管廊、野外杆塔、厂房角落怎么把数据送出去本身就是问题设备种类五花八门有的走Modbus RTU有的走LoRa有的走BLE如果让每种设备直接对接云平台那云端要适配的协议得多到什么程度。这时候无线网关的价值就体现出来了。它在物理上贴近设备侧负责把各种短距无线协议LoRa、Zigbee、BLE、Wi-Fi统一收上来做协议转换、数据清洗、边缘缓存再通过有线以太网、4G蜂窝或者Wi-Fi把数据送到云平台。云端只需要面对一个稳定、标准、不需要关心底层射频细节的“智能通道”。拿我这个项目来说现场部署了40多个LoRa温湿度传感器和一批BLE定位标签。如果每个传感器都直接连Azure IoT Hub先不说成本和功耗光是设备身份管理、连接保活就能让人崩溃。而通过无线网关统一接入云端只看到两台网关设备传感器数据全部作为网关的属性上报整个链路清爽太多。1.2 选定Azure-Ready技术栈的选型逻辑既然网关是数据汇聚点那“网关往哪里送数据”就成了架构决策的核心。这个项目之所以锁定Azure IoT技术栈而不是自建MQTT Broker也不是选其他云厂商主要基于三方面考量。第一托管服务的成熟度。Azure IoT Hub在设备身份管理、消息路由、设备孪生、文件上传、OTA这些能力上非常完整团队不需要自己维护一套高可用的MQTT集群。尤其是设备孪生Device Twin这套机制对远程配置网关参数来说简直是刚需省掉了自建远程命令通道的成本。第二与现有企业系统集成顺畅。项目后端的数据分析、可视化已经跑在Azure上IoT Hub产生的遥测数据可以直接路由到Storage Accounts、Event Hubs再交给Stream Analytics或Power BI处理链路短且无需额外开发适配层。第三团队技术积累匹配。团队里对C#、Python比较熟Azure IoT SDK在这两种语言上支持度很高从设备端到服务端都有现成库可用。相比之下如果自建EMQX一套规则引擎虽然灵活但上线周期和运维成本都会明显拉长。我也简单对比过AWS IoT Core的方案功能上确实很强大AWS IoT Greengrass在边缘计算上做得也很出色但有一点让我很纠结当时团队在Azure上的既有服务已经不少AD、Functions、Power BI都在Azure为了一个网关接入再去引入第二套云生态身份管理、账单、日志系统全都要分开看没必要。下表是我当时做选型时的对比记录对比维度自建MQTT规则引擎AWS IoT CoreAzure IoT Hub运维成本高集群自维护低托管服务低托管服务设备身份管理需要自研完善完善设备孪生能力无现成方案有Shadow有Device Twin边缘计算无Greengrass强IoT Edge可用团队技术栈匹配中中高与既有系统集成需要自研一般顺畅对于大多数中小规模物联网项目云厂商托管IoT服务都是比自建更务实的选择省下的时间精力可以花在业务逻辑和现场落地这类真正产生价值的事情上。1.3 这套架构的适用边界聊完了优点也得说清楚这套方案在什么情况下不那么合适。我个人的判断是如果设备数量很少比如就几个试验设备而且都是开发板级别的验证那把设备直连云平台就够了不需要网关这层中间代理。如果业务场景对数据实时性要求极高且不允许任何云中断影响比如工业控制回路那就算Azure IoT Hub做了可用性保障网关侧的本地自治和断网续传能力也必须设计得非常强这时的复杂度主要不在云而在网关边缘侧。如果设备协议非常统一、物理位置也不分散那网关的协议转换价值就体现不出来。换句话说无线网关Azure这套组合最合适的场景是“多协议、多厂商、位置分散、环境复杂、需要统一纳管”的生产型物联网项目。做架构前先对照自己的真实情况别为了上网关而上网关。2. 无线网关硬件与通信层的核心技术拆解2.1 网关主控怎么选Linux SoC还是MCURTOS网关的硬件选型是项目成败的地基。我在第一版方案里考虑过用STM32MP1这类MCUMPU异构方案做主控后来和团队聊完还是选了以NXP i.MX8M Mini为核心、跑定制Linux的方案现在回头看这个决策是对的。原因有几个。第一网关要跑的协议栈实在太复杂了下行要同时驱动LoRa收发、扫描BLE广播包上行要走MQTT/TLS、维护设备孪生同步这些如果用RTOS裸写开发周期会指数级上升。第二后续想升级固件、加调试接口、跑个本地Python脚本做数据预处理Linux环境能把所有事情都简化。第三安全方面OpenSSL、可信执行环境、密钥存储这些在Linux下已经有成熟方案MCU方案搞起来要费很大劲。当然MCU方案也不是一无是处成本低、实时性好、功耗低。但它更适合那些只做单一协议转换、功能固定的透传网关。如果你的网关要对接多种无线协议同时还要和云平台做双向通信那别犹豫直接上Linux SoC。内存建议至少512MB DDR存储要有8GB eMMC以上这样跑系统、存日志、做缓存都够用。2.2 下行无线协议的接入全解析网关集成哪些无线模块取决于现场传感器用什么协议。我这套网关同时支持了LoRa、Zigbee、BLE三种这也是很多实际项目里最常见的组合。LoRa接入LoRa的优势是远距离、低功耗、穿透力强适合分布在几百米范围内的传感器节点。网关侧需要一个LoRaWAN网关模块或者串口透传模块比如常见的SX1301方案、E22-400M系列。需要注意LoRaWAN里面有Join流程、帧加密如果传感器端用的是标准LoRaWAN协议栈网关必须实现Network Server的部分功能或者把LoRaWAN数据包转发给云上的LoRaWAN Server。如果传感器端用的是纯LoRa点对点透传模式那网关要自己定义帧格式包括设备地址、消息类型、数据负载、CRC校验。项目测试时我用过纯LoRa透传模式帧格式自己定调起来更灵活但对端设备和网关的约定要非常明确否则后期很容易踩数据解析的坑。Zigbee接入Zigbee主要用在智能楼宇的灯光、插座、门磁场景。网关侧需要Zigbee协调器模块如基于CC2530/CC2652的模块负责建立Zigbee网络、维护设备入网、接收属性上报。Zigbee有个特点设备入网需要协调器允许加入而且网络里设备类型多协调器、路由器、终端设备调试时要特别注意终端设备休眠唤醒后上报延时的处理。BLE接入BLE应用最灵活但难点在数据采集方式。比如我们这个网关用BLE接收定位标签的广播包实现室内人员定位。BLE广播包不需要连接网关只要扫描附近所有MAC地址的广播帧解析RSSI值就能做粗略定位。但是要注意如果现场广播设备非常多扫描窗口和扫描周期需要调优否则很容易丢包。我当时调BLE扫描参数踩了不少坑这个后面在问题排查章节里细说。网关内部要把三套协议的数据统一成一份内部数据模型然后再映射到云的遥测消息。这个映射关系一定要在项目一开始就设计好否则每个协议各出一套数据结构云端解析的代码会变得异常混乱。2.3 上行连接与断网缓存机制网关的上行链路通常有两条有线以太网和4G蜂窝。在有固定网络条件的地方优先用有线稳定且便宜在野外或移动场景4G是必要的备份链路。我做的这个网关两个网口都留了4G模块用的常见的CAT1模块成本低、覆盖好速率对物联网场景绰绰有余。这里要特别强调断网缓存。现实中工厂车间的交换机不会永远稳定移动网络在隧道里也会断。如果网关一断网就把数据丢了用户是绝对无法接受的。我设计的缓存策略很简单但有效遥测消息先写本地SQLite数据库云端确认收到后再从库里标记删除如果离线超过一定时长启动定时批量补传。本地缓存表设计大概是这样字段名类型说明idINTEGER PRIMARY KEY AUTOINCREMENT自增主键device_idTEXT传感器设备IDpayloadTEXT原始数据JSONcreated_atINTEGER采集时间戳uploadedINTEGER DEFAULT 0是否已上传云端upload_timeINTEGER上传完成时间这个策略看起来简单但有几个坑要注意批量补传时如果还是逐条发MQTT消息云端的消息顺序和时序会很乱补传的消息最好带上原始采集时间戳而不是发送时间这样云端分析时数据才真实。补传频率也要限制避免断网恢复那一刻瞬间产生大量请求把自己打满。3. Azure IoT Stack核心组件与对接要点3.1 先摸清IoT Hub的几个核心概念Azure IoT技术栈里IoT Hub绝对是最核心的组件其它都是围绕它展开的。它对设备提供三件事设备身份注册与认证、双向收发消息、设备孪生同步。设备身份这块IoT Hub里的每个设备都需要有一个唯一的Device ID认证方式可以选对称密钥SAS Token、X.509证书或者TPM证明。云平台侧维护设备注册表设备连上来时会校验身份。这里有个容易踩的坑很多人会直接在Azure门户里手动创建设备设备少还行一旦批量部署网关就非常低效。正确的做法是用Device Provisioning ServiceDPS做批量注册设备第一次上电时通过DPS自动完成注册和分配到指定IoT Hub整个过程不需要人工参与。消息通信这块IoT Hub支持MQTT、AMQP、HTTPS协议。嵌入式场景最常用MQTT端口8883走TLS。消息分两类设备到云遥测数据和云到设备命令。云端发命令时可以用直接方法Direct Method也可以用设备孪生的期望属性下发后面讲实操时我会详细区分。设备孪生是Azure IoT非常独特的一个设计。它其实是云端的设备状态JSON文档分为tags标签、desired期望状态、reported上报状态三部分。网关可以把当前工作模式、固件版本、网络信号状态写到reported里云端通过修改desired来远程调整网关配置。这套机制做远程运维太方便了我在项目里连网关的日志级别变更都是通过设备孪生下发的完全不需要远程SSH。3.2 认证方案选型SAS Token还是X.509证书这是每个IoT项目都要回答的问题。我先说结论如果你的网关最终要批量量产强烈建议用X.509证书而不是SAS Token。SAS Token的原理是设备使用对称密钥加上过期时间生成一个签名串。优点是SDK支持好、上手快、代码量少。但缺点也很明显对称密钥一旦泄漏攻击者可以冒充设备而且设备多了之后密钥管理、轮换都是麻烦事。实际上写代码时连接字符串里那串Key就是SAS的根密钥只要拿到它谁都能伪造这个设备。X.509证书则采用非对称加密私钥存储在设备安全区域云端验证证书签名。配合DPS可以实现“一机一证”自动注册证书过期前还可以远程轮换。即使某台设备的私钥泄漏也不会影响其它设备。我当时做的选型表格对比项SAS TokenX.509证书开发难度低中安全性中高密钥泄露影响影响单设备/组设备可控可吊销批量生产适配一般好证书轮换不适用支持DPS自动注册支持支持有一点要明确X.509不是银弹。证书本身有有效期过期了设备就连不上云如果没有自动续期流程运维会比SAS还痛苦。我的做法是在网关里部署一个证书管理模块启动时检查证书剩余有效期不足30天时自动通过DPS完成证书续签流程。这个机制在生产环境跑了很久基本没出过问题。3.3 消息路由与下游数据加工IoT Hub收到设备上报的消息后不会自动帮你存数据库。它的机制是通过消息路由Message Routing把消息按照规则转发到不同的内置端点或自定义端点。常用端点包括Storage Account冷存储、Event Hub热数据流、Service Bus Queue业务处理。路由规则的配置里其实有个很多人忽略的细节路由查询语句是以消息属性和消息体为筛选条件的。如果要在路由里区分消息类型建议设备端在上报时加上消息属性比如message_typetelemetry而不是在消息体里解析。因为消息体解析在路由阶段非常低效。我当时的消息结构大概是这样设备端MQTT发布时主题是devices/{deviceId}/messages/events/并且设置应用属性property_typetelemetry或property_typeevent。路由规则再根据属性分发到不同数据管道。数据到了Event Hub之后通常是交给Azure Stream Analytics做实时分析或者由Azure Functions消费来做业务联动。如果只是做历史数据留存和分析直接用路由到Storage Account然后定时跑Azure Data Factory或Databricks也是一个性价比很高的方案。这些下游服务的组合方式很灵活核心要义是IoT Hub只负责可靠接入和消息传递剩下怎么用你的业务自己做。4. 从零对接实操网关连上Azure IoT Hub的完整过程4.1 云上环境准备创建Hub、DPS与设备注册第一步当然是准备Azure账号并创建资源。登录Azure门户搜索并创建IoT Hub选好订阅、资源组和区域。区域选择上建议选离你的网关实际部署位置最近的那个这个对链路延迟和合规都有影响。IoT Hub创建好之后在“管理”页面记下“主机名”后续设备连接要用的就是它。然后创建DPSDPS需要关联到刚才的IoT Hub。DPS创建一个“ enrollment group”如果网关用X.509证书就直接把CA证书上传到DPS并完成证明验证。如果你只是快速验证用对称密钥的 individual enrollment也行。由于我这里用的X.509证书方案所以还需要自己生成一套测试证书。常见的做法是用OpenSSL自签一个CA证书再给每台网关签发设备证书。实测时可以直接用Azure提供的示例脚本生成测试证书azure-iot-sdk-c里有一组工具脚本生成的证书包含证书主体和设备ID的绑定关系。证书生成完在DPS里上传CA证书然后用设备证书连接。在DPS里做操作时有一个很容易被忽略的字段叫“Device ID”如果你的设备证书CNCommon Name和Device ID配合不好注册时会报错。我的经验是设计证书CN时就统一用它做设备标识比如CNgw-001DPS里注册用的Device ID也是gw-001这样整个链路的一致性最好。4.2 网关端代码编写使用Python SDK快速接入网关端我用的语言是Python因为团队熟悉而且Azure IoT SDK for Python对MQTT和Device Twin的支持非常完整。如果你的网关内存和CPU极有限也可以考虑C SDK但代码量会大很多。先安装SDKpip install azure-iot-device然后编写一个最小可运行的上云程序核心步骤包括通过DPS进行身份注册、建立MQTT连接、发送遥测消息、监听云到设备命令、同步设备孪生。import asyncio import json import random from azure.iot.device.aio import ProvisioningDeviceClient from azure.iot.device.aio import IoTHubDeviceClient from azure.iot.device import Message ID_SCOPE 0ne00xxxxx # DPS 的 ID Scope REGISTRATION_ID gw-001 # 设备注册 ID # 1. 用 X.509 证书通过 DPS 注册获取 IoT Hub 连接参数 async def register_device(): provisioning_client ProvisioningDeviceClient.create_from_x509_certificate( provisioning_hostglobal.azure-devices-provisioning.net, registration_idREGISTRATION_ID, id_scopeID_SCOPE, x509{ cert_file: /path/to/device-cert.pem, key_file: /path/to/device-key.pem, }, ) registration_result await provisioning_client.register() print(f注册成功分配到的 Hub: {registration_result.registration_state.assigned_hub}) return registration_result.registration_state.assigned_hub async def send_telemetry(client, device_id, temperature, humidity): payload json.dumps({ device_id: device_id, temperature: temperature, humidity: humidity, ts: int(time.time() * 1000), # 使用毫秒级UTC时间戳 }) message Message(payload) message.content_encoding utf-8 message.content_type application/json message.custom_properties[message_type] telemetry await client.send_message(message) async def main(): assigned_hub await register_device() # 2. 使用返回的 Hub 信息创建设备客户端 client IoTHubDeviceClient.create_from_x509_certificate( hostnameassigned_hub, device_idREGISTRATION_ID, x509{ cert_file: /path/to/device-cert.pem, key_file: /path/to/device-key.pem, }, ) await client.connect() # 3. 循环上报遥测 for i in range(10): await send_telemetry(client, REGISTRATION_ID, 25.6 i, 60 i) await asyncio.sleep(5) await client.disconnect() if __name__ __main__: asyncio.run(main())这段代码里最关键的地方是设备第一次启动时通过DPS自动完成注册和分配拿到属于自己那台IoT Hub的主机名然后创建MQTT客户端连接。这个流程完全自动化即使是几千台网关同时上电也能扛得住。SDK默认使用的MQTT库是paho-mqtt底层会处理TLS握手你只需要把证书路径配置正确即可。我在真实环境调试时发现证书文件路径如果权限不对比如root用户才能读程序会直接报错所以部署时要把证书和SDK进程的用户权限设计好。4.3 遥测消息与设备孪生的实际效果验证程序跑起来之后怎么确认云端真的收到了消息我用的是Azure CLI加门户双重验证。首选是用Azure CLI快速查看消息流执行下面的命令能看到最近流入IoT Hub的消息az iot hub monitor-events --hub-name {your-hub-name} --device-id gw-001如果这条命令能持续输出消息内容说明从网关到云的链路已经通了。如果要验证消息落库和路由链路那就在IoT Hub的“消息路由”里配置一个到Storage Account的端点路由查询语句设为message_type telemetry过几分钟到存储容器里查一下有没有对应的blob文件生成。能查到文件就说明整条数据链路彻底打通了。设备孪生验证相对简单在门户里打开对应设备页面切到“设备孪生”页签能看到reported属性是否有更新。我通常会让网关开机时上报一段版本信息和配置参数{ reported: { model: GW-2000, firmware: 1.2.3, network: { rssi: -65, mode: 4g } } }云端能实时看到这台网关的信号强度、固件版本对远程排障非常有用。再配合desired属性下发可以实现类似“远程开启调试日志”这种操作。4.4 云到设备命令如何远程更新网关参数除了数据上报物联网项目里经常需要远程控制设备。Azure IoT Hub提供两种主要方式直接方法和设备孪生期望属性。我的建议是讲究实时性、即时反馈的操作用直接方法比如重启网关、立即上报一次数据不追求实时性、需要持久化的配置用设备孪生desired比如修改上报周期、切换工作模式。设备孪生下发配置的一个优势是即使设备离线配置也会保留在云端设备重新上线时自动同步。这类“操作后不一定要立刻生效”的需求太适合用孪生了。远程更新网关的数据上报周期就是个非常典型的例子把desired里的report_interval改成60网关几分钟内会收到并应用然后把reported也更新为60云端就能确认这次配置修改成功。直接方法的代码也比较简单SDK里给client添加一个直接方法的回调async def reboot_handler(payload): print(f收到重启命令参数{payload}) await device_client.shutdown() os.system(reboot) return {status: 200, payload: {result: 重启中}} client.on_method_request_received reboot_handler这里有一点要特别提醒直接方法在没有超时时间内如果设备不在线云端会返回失败所以它不适合用来做离线设备的配置变更。离线场景一定走设备孪生。5. 常见问题与排查技巧实录5.1 连接与认证类问题速查表做网关对接时90%的时间可能都在跟连接问题和认证问题打交道。我整理了一份自己实际遇到过的问题记录现象根本原因解决方法MQTT连接返回401 Unauthorized设备密钥错误或SAS Token过期检查连接字符串、重新生成TokenDPS注册失败提示未授权CA证书未验证或Device ID与证书CN不一致在DPS中完成证书验证统一CN与Device ID连接被拒绝提示IoT Hub配额不足免费F1层有消息数量限制升级付费层级或减少测试消息频率设备显示为Disabled设备在注册表里被禁用到门户启用设备TLS握手失败时钟偏差太大或证书链不完整校准设备RTC时间检查设备证书链是否包含CA5.2 无线链路与数据质量问题处理无线网关最头疼的问题常常不在云端而在“最后一公里”的射频链路上。我在这个项目里遇到的典型问题之一是LoRa节点上报的数据偶发丢失。排查过程非常曲折先看网关日志确实收到了LoRa数据包但网关在转发到云端的路上丢了。后来把MQTT的QoS从0改成1问题就解决了。看似简单但说明设计初期对链路可靠性的预估不足消息重传机制从一开始就应该考虑到。还有一个问题是BLE扫描丢包。现场有两个设备离网关很近RSSI在-40dBm左右但扫描到的次数反而少。后来查资料才知道BLE扫描参数里扫描窗口scan window和扫描间隔scan interval的设置会影响不同距离设备的响应概率。最终把扫描窗口调大、扫描模式改成主动扫描丢包率才降下来。数据质量问题也值得专门说。网关收到的LoRa帧里如果含有传感器原始字节解析时一定要做CRC校验和长度校验否则数据错位会污染整条数据链路。我在调试时还遇到过一次传感器上报的时间戳是本地时间而云端以为是UTC导致图表里的曲线整体偏移了8小时。这个问题很隐蔽排查到最后才对齐所以建议在数据模型里用标准UTC时间并在字段名上明确标注时区。5.3 部署后的安全加固维护经验网关一旦上线维护期的工作量绝对不亚于开发期。这里分享几条我踩过坑之后总结出来的经验密钥和证书一定要有轮换机制。X.509证书不是装上去就一劳永逸的必须设计过期前的自动续签。我在网关里写了一个后台任务每天检查证书有效期发现不足30天就自动向DPS发起续签流程并替换新证书。最早没有这个机制的时候有过一台网关因为证书过期在周末掉线当时跑到现场去换证书那种体验再也不想有了。日志管理必须有远程采集通道。网关日志是故障排查的第一手资料但你不能每一个问题都跑到设备跟前看日志。我用的是把网关日志同时输出到本地文件和云端事件流云端可以用Azure Log Analytics统一查询。设计日志等级时生产环境把调试日志关掉只保留info和error级别需要排查时再通过设备孪生远程调高日志级别。固件OTA要谨慎设计回滚机制。网关固件升级是整个系统里风险最高的操作之一一旦升级到一半断电网关可能变砖。我的做法是采用A/B分区方案新固件先写到备用分区校验通过后切换启动分区如果启动失败或上报的版本号不对自动回滚到上一个分区。这个机制虽然占用了一倍的存储空间但带来的确定性是值得的。5.4 数据量突增时的性能调优最后再聊一个容易被忽视、但线上必踩的坑当网关下的传感器数量增多或者上报频率升高网关自身和云平台都会出现性能瓶颈。我遇到过网关本地CPU被打满的情况原因不是云平台而是网关在收到传感器数据后做JSON序列化和MQTT发布时处理逻辑是串行的。后来改成多线程加队列模型才解决问题。网关内部数据缓冲的容量也要根据现场实际情况估算比如网关下有200个LoRa节点、每秒上报一次缓存至少要能扛住断网重启后5分钟的数据量否则缓存溢出丢数据就是必然。云端的性能调优主要体现在IoT Hub层级的选取和分区设计上。IoT Hub有免费层、标准层等不同规格每层的消息上限和分区数不同。如果你的网关数据频繁达到配额不仅会影响当前设备的消息收发还可能导致IoT Hub的限流进而影响其它设备。建议上线前用消息量上限做压力测试确认层级选择和消息路由方式都合理。我后来把路由到Event Hub的路径走的是“IoT Hub内置端点”省掉了消息转发时的一些开销整体吞吐明显提升。最后分享一点我的个人体会从最初的网关硬件组装到最终在Azure上跑通完整的设备管理、数据上报和远程运维这个项目让我对“无线网关接入云平台”这件事有了更踏实的认知。很多人觉得物联网的难点在“云”或者在“设备”但真正项目落地时你会发现瓶颈往往在中间这层网关——它既要把各种无线协议收拢又要保证数据传输的可靠性还要扮演远程运维的代理网关。我的体会是做这类项目一定先把无线链路和本地缓存做扎实再想着怎么上云顺序反了后面会非常被动。另一个很重要的经验是安全要从第一天就设计进去X.509证书加DPS自动注册这套组合虽然前期配置稍麻烦但在规模化部署时带来的好处远超那点学习成本。最后分享一个小技巧网关的远程管理能用设备孪生下发的配置就不要用远程SSH命令这套机制在设备数量多起来之后会特别省心。
返回列表