从0开始构建物联网平台.
课程背景
物联网应用开发,并不像 Web 开发那样有固定的模式和框架可以学习,开发者往往还是需要从协议这一层慢慢往上搭积木,学习曲线比较陡。本课程结合物联网应用开发常用的设计模式以及作者多年的开发经验,带你从 0 开始搭建一个物联网平台,希望本课程所体现的架构和思路能够帮助你少走弯路、少踩坑。
课程亮点
从 0 开始搭建专属物联网平台
大量实战代码,手把手逐行讲解使用开源组件,掌握大厂设计思路和实现逻辑
“踩坑”经验沉淀,教你快速「搭积木」
优化物联网应用开发学习曲线覆盖物联网开发中 80% 的场景以及可能遇到的设计和架构问题你能收获什么?学习物联网应用的常用架构和设计模式学会独立设计和开发可以用于支撑多个物联网应用的物联网平台学会使用和编写 EMQ X 的插件获得一套完整的、可运行的物联网平台代码,包括服务端和设备端 SDK前置知识
学习和熟悉 MQTT 协议的基本概念和特性,对阅读本课程有非常大的帮助。
作者介绍
付强,某物联网 Startup 联合创始人兼 CTO,之前就职于趋势科技,诺基亚中国,在物联网领域从业多年,曾经在德国、硅谷的 Startups 工作过。
适宜人群物联网应用开发者物联网架构师物联网平台开发者课程目录
课程内容开篇词:开发物联网应用,光会 MQTT 还不够
导读
大家好,我是付强,我现在是一家物联网 Startup 的联合创始人兼 CTO,在自己创业之前,我曾经在趋势科技和诺基亚工作过。从 2011 年我在硅谷参与的第一个物联网项目开始算起,到 2015 年开始在物联网方向创业并运营到现在,从 0 到 1,1 到盈利,我在物联网这个行业已经摸爬滚打快 8 年了。
我的上一门课程《MQTT 协议快速入门》详细讲解了 MQTT 协议及其各种特性。在课程的交流群中,读者们也提了很多问题,除了关于 MQTT 协议本身的内容以及特性相关的问题之外,还有很多问题是关于物联网软件设计和架构方面的,比如:
我应该如何管理我的设备和设备的状态?业务服务端应该怎么接收、处理和存储来自设备的数据?我的设备数量很多, Broker 端应该怎么架设来确保性能和扩展性?我的设备处理能力有限,除了使用 MQTT 协议以外,还有没有其他的选择?……
这让我意识到,单单学会和熟悉 MQTT 协议,离开发和架构一个成熟的物联网产品还是有一段不小的距离,其实仔细想想,这也没有什么不对的:拿 Web 开发做一个类比,我们只学习 HTTP 协议,就能够开发一个成熟的网站或者基于 Web 的服务吗? 答案也是否定的。
回想一下我们是怎么学习 Web 开发的。首先我们会了解一下 HTTP 协议,然后选择一个框架,比如 Java 的Spring Boot、 Python 的 Django、Ruby 的 Rails 等。这些框架提供固定的模式,对软件进行了高度的抽象和分层,比如集成了一些 Web 开发的 Good Practice; 你会知道在 Model 层处理业务的逻辑,用 ORM 来进行数据库操作,在 Controller 层处理输入输出和跳转,在 View 层渲染 HTML 页面,这样一个网站和 Web 服务就能够很快被开发出来,除了性能优化的时候,你几乎不用去想 HTTP 协议的细节。
回到物联网开发,抛开设备端的异构性,单说服务端的架构这块,并不像 Web 开发这块有一个 Well Known 的模式、架构或者开发框架。开发者往往还是需要从协议这一层慢慢往上搭积木,学习曲线还是比较陡的。
我的经历
我们自行开发的物联网平台实现了设备的管理和接入,设备数据的存储和处理,并抽象和封装了基于 MQTT 协议的数据传输,比如设备的数据上报和服务端的指令下发等,提供了业务服务端使用的服务端 API,和设备端使用的设备端 SDK, 业务服务器和设备不再需要处理数据传输和接入等方面的细节,它们甚至不知道数据是通过 MQTT 协议传输的,这一切对业务服务器和设备都是透明的。
在这个过程中,我踩了很多坑,同时也积累了一些物联网平台在架构和设计模式等方面的经验。在这门课程中,我想把这些物联网平台架构以及设计方面的知识和经验分享给大家,这应该可以覆盖大家在物联网开发中 80% 的场景和可能遇到的设计和架构问题。
推荐阅读 ?《从 0 开始搭建 IoT 平台》
如何学习
2019 年的阿里云 IoT 平台功能已经非常强大了,在本门课程中,我们将使用开源的组件,从第一行代码开始,一步步地实现一个具有阿里云 IoT 平台大部分的功能的”物联网平台”,在这个过程中穿插讲解在物联网平台开发中可以用到的模式和架构的选择, Pros and Cons,以及一些 Best Practice 等。与《MQTT 协议快速入门》侧重协议内容和理论不同,本门课程包含大量的实战代码,毕竟代码是程序员之间交流的最好语言。
这里给这个”物联网平台”起了一个 Codename: Maque IotHub,简称 IotHub。
本课程属于实战性课程,所以不会再详细地讲解 MQTT 协议的概念和特性了,预先学习和熟悉 MQTT 协议的基本概念和特性,对阅读本课程有非常大的帮助。你可以访问 mqtt.org 查阅协议的文档,也可以通过阅读我的《MQTT 协议快速入门》来进行快速学习。
课程特色
目前物联网开发的的实战课程,特别是成体系的实战课程是很少的,本课程涵盖了从物联网平台到设备端开发的大量场景和设计模式,并不像其他教程那样只是罗列知识点。本课程各节内容之间就像搭积木一样关联性很强,从第一行代码开始搭建一个物联网平台,轻理论而重实战,专注于你无法在互联网上找到参考的实战内容,干货十足。
课程介绍
开篇词:开发物联网应用,光会 MQTT 还不够附录:如何运行 Maque IotHub第一部分(第 1-1 ~ 1-9 课):设备生命周期管理
第二部分(第 2-1 ~ 2-6 课):上行数据处理
在课程的第二部分,我们会设计和实现 IotHub 的上行数据处理功能,在这部分我们会学习 EMQ X 的 Hook 机制,以及它如何为 IotHub 的开发带来便利性,同时也会第一次对 IotHub 中的 MQTT 主题名进行规划,把 MQTT 的 Broker-Client 模式转换为 Server-Client 模式。
第三部分(第 3-1 ~ 3-5 课):下行数据处理
在课程的第三部分,我们会设计和实现 IotHub 的指令下发功能,在这部分我们会学习使用 EMQ X 的监控管理 API,并对指令下发的主题进行规划,实现可靠的指令下发流程。
第四部分(第 4-1 ~ 4-15 课):进一步抽象
第五部分(第 5-1 ~ 5-6 课):扩展 EMQ X
在前面的课程中,我们使用大量的 EMQ X 自带插件功能来完善 IotHub, 在这部分课程中,我们将学习如何编写一个 EMQ X 插件。通过这部分的学习,读者将会掌握根据自己的业务逻辑来扩展 EMQ X 的能力。
第六部分(第 6-1 ~ 6-2 课):CoAP
适合阅读的人群
本课程适合以下人群阅读:
物联网应用开发者物联网架构师物联网平台开发者
课程寄语
在我的另一篇达人课《MQTT 协议快速入门》,我尝试着用一门课来解答大家在物联网应用设计和开发上遇到的问题,在交流中发现,大家的问题不只局限在协议上。这是也促成我编写这篇课程的原因之一。
物联网应用开发不像 Web 开发那样有固定的模式和框架可以学习,本课程实现阿里云 Iothub 的部分功能,并结合我在设计和实现一个已上线运行多年的物联网平台的经验,来阐述在物联网应用中常用的设计模式和思路,目的也是希望大家在学习本课程之后,可以少走点弯路、少踩点坑。如果你不需要实现一个物联网平台(比如直接使用云服务商提供的物联网平台)也没关系,这门课所体现的架构和思路也能帮助你更好地设计物联网应用。
我希望读者在学习完本课的内容后,能够将学到的设计思路和模式运用到实际工作中去,希望本课程能够解答你在物联网应用开发中能遇到大部分设计和实现的问题。毕竟 5G 时代已经来临,物联网的发展势头会越来越快。
学完后的收获
熟悉物联网应用的常用架构和设计模式学会独立设计和开发可以用于支撑多个物联网应用的物联网平台学会使用和编写 EMQ X 的插件获得一套完整的、可运行的物联网平台代码,包括服务端和设备端 SDK第 1-1 课:准备工作台
在本课中,我们将安装开发物联网平台时使用到的组件,并把物联网平台的开发环境搭建起来。
安装组件
首先在开发机上面安装开发需要的组件。
MongoDB
MongDB 是一个基于分布式文件存储的数据库,我们会把 MongoDB 作为物联网平台主要的数据存储工具。
可以在这里找到 MongoDB 的安装文档,根据文档在对应的系统上安装和运行 MongoDB。
Redis
Redis 是一个高效的内存数据库,物联网平台会使用 Redis 来实现缓存和简单的队列功能。
请根据这里的文档,在对应的系统上安装和运行 Redis。
Node.js
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,我们会使用 Node.js 来开发物联网平台的主要功能。
请根据这里的文档在对应的系统上面安装 Node.js。
RabbitMQ
RabbitMQ 是使用 Erlang 编写的 AMQP Broker,物联网平台使用 RabbitMQ 作为队列系统实现物联网平台内部以及物联网平台到业务系统的异步通信。
请根据这里在对应的系统上按照和运行 RabbitMQ。
EMQ X
EMQ X 是一个使用 Erlang 编写的 MQTT Broker,物联网平台使用 EMQ X 来实现 MQTT/CoAP 协议接入,并使用 EMQ X 的一些高级功能来简化和加速开发。
可以在这里找到 EMQ X 的安装文档,安装完毕之后,在控制台运行:
/bin/emqx start
如果命令行输出为emqx 3.2.0 is started successfully!
那说明 EMQ X 已经成功安装并运行了。
本课程在编写时,使用的版本为EMQ X Broker V 3.2.0。 EMQ X Enterprise为 EMQ X 的付费版本,注意不要安装错了。
那么开发物联网平台需要的组件就安装完了,接下来我们简单介绍一下物联网平台的各个组成部分。
首先给这个物联网平台取一个代号,就叫它 Maque IotHub 吧,寓意麻雀虽小,五脏俱全。 接下来我们把后续课程中会出现的实体都定义一下:
Maque IotHub:我们将要开发的物联网平台,简称 IotHub。Maque IotHub Server API:Maque IotHub 的服务端 API,以 Restful API 的形式将功能提供给外部业务系统调用,简称 Server API。Maque IotHub Server:Maque IotHub 的服务端,包含了 Server API 和主要的 IotHub 服务端功能代码,简称为 IotHub Server。业务系统:指实现特定物联网应用服务端的业务逻辑系统,它通过调用 Maque IotHub Server API 的方式来控制设备/使用设备上报的数据,Maque IotHub 为它屏蔽了和设备交互的细节。Maque IotHub DeviceSDK:Maque IotHub 提供的设备端 SDK,设备通过调用 SDK 提供的 API 接入 Maque IotHub,并和业务系统进行交互,简称 DeviceSDK。设备应用代码:实现设备具体功能的代码,比如打开灯、在屏幕上显示温度等,它调用 Maque IotHub DeviceSDK 来使用 Maque IotHub 提供的功能。
在后续的课程中就用 IotHub 来指代我们开发的这个物联网平台,用 DeviceSDK 来指代设备端的 SDK 代码,用 IotHub Server 来指代物联网平台的服务端代码。
项目结构
本课程会使用两个 Node.js 的项目来进行开发,分别是物联网平台的服务端代码 IotHub Server 和设备端代码 DeviceSDK。
Maque IotHub Server
服务端代码以一个 Express 项目作为开始, Express 是一个 Node.js 轻量级的 Web 开发框架,非常适合开发 Restful API,项目的结构如下图所示:
这个项目包含了 Maque IotHub Server API 以及 Maque IotHub 服务端的一些其他功能。
Maque IotHub DeviceSDK测试 MQTT 连接
首先我们来验证一下 EMQ X Broker 是否已经配置正确,并可以接受 MQTT 连接了。
同样地,这里使用一个 Node.js 项目开发 Maque IotHub DeviceSDK 代码。
在package.json
中添加 MQTT 的 Node.js 库:
"dependencies": { "mqtt": "^2.18.8" }
然后运行npm install
。
我们可以写一小段代码来测试一下 MQTT 连接:
//test_mqtt.jsvar mqtt = require('mqtt')var client = mqtt.connect('mqtt://127.0.0.1:1883')client.on('connect', function (connack) { console.log(`return code: ${connack.returnCode}`) client.end()})
如果不出意外的话,控制台会输出:return code: 0
。
重新组织代码
为了方便设备应用代码的开发, DeviceSDK 会把和 MQTT 相关代码,以及同Maque IotHub Server交互的相关的代码进行封装,这里我们实现一个类IotDevice
作为 DeviceSDK 的入口:
//iot_device.js"use strict";var mqtt = require('mqtt')const EventEmitter = require('events');class IotDevice extends EventEmitter { constructor(serverAddress = "127.0.0.1:8883") { super(); this.serverAddress = `mqtts://${serverAddress}` } connect() { this.client = mqtt.connect(this.serverAddress, { rejectUnauthorized: false }) var self = this this.client.on("connect", function () { self.emit("online") }) this.client.on("offline", function () { self.emit("offline") }) this.client.on("error", function (err) { self.emit("error", err) }) } disconnect() { if (this.client != null) { this.client.end() } }}module.exports = IotDevice;
这段代码做了这几件事。
封装了 MQTT Client 的 connect 和 disconnect 。通过设定 MQTT 连接地址为mqtts://127.0.0.1:8883
的方式,在传输层使用 SSL。 EMQ X 默认使用一个自签署的的证书,所以我们需要设定rejectUnauthorized: false
。通过 Events 和设备应用代码进行交互。
那么使用IotDevice
类进行连接设备应用代码如下:
var device = new IotDevice()device.on("online", function () { console.log("device is online") device.disconnect()})device.on("offline", function () { console.log("device is offline")})device.connect()
Maque IotHub DeviceSDK 项目结构如下图所示:
在 sdk 目录中是 DeviceSDK 的代码。在 samples 目录中是调用 DeviceSDK 的示例代码。
注意!!! 为了方便学习和技术交流,特意创建了读者群,入群方式放在 第 1-5 课 文末,欢迎已购本课程的同学入群交流。
在本节中,我们将设计 IotHub 的设备认证机制。
设备三元组
EMQ X 在默认的情况是允许匿名连接的,所以在上一节课程中,IotDevice
类在连接 MQTT Broker 的时候没有指定 username 和 password 也能成功。
阿里云 IoT 平台用一个三元组(ProductKey, DeviceName, Secret)来标识一个逻辑上的设备,ProductKey 是指设备所属的产品,DeviceName 用来标识这个设备的唯一名称,Secret 是指这个设备连接物联网平台使用的密码。我认为这是一个很好的设计,因为即使在同一家公司内部,往往也会有多个服务不同业务的物联网产品需要接入,所以多一个 ProductKey 对后续的主题名、数据存储和分发等进行一个区分是很有必有的。
Maque IotHub 将使用类似的三元组(ProductName, DeviceName, Secret)来标识逻辑上的一台设备。ProductName由业务系统提供,可以是一个有意义的 ASCII 字符串,DeviceName 由 IotHub 自动生成,(ProductName, DeviceName)应该是全局唯一的。
这里我们约定,对一个设备(ProductName1, DeviceName1, Secret1),它接入 Maque IotHub 的 username 为ProductName1/DeviceName1
,password 为Secret1
。
为什么说三元组标识的是逻辑上的一台设备而不是物理上的一台设备? 比如说:移动应用接入 Maque IotHub 来订阅某个主题,假如有一个用户在多个移动设备上用同一个账号登录,他使用的应该是同一个三元组,因为他订阅的消息在每个设备上都应该能收到,那么在这种情况下一个三元组实际上是对应多个物理设备。后面我们再来讲怎么来区分物理设备。
为什么要用”/”做分隔符,这里先不作说明,在讲到下行数据处理的部分再来解释。
EMQ X 认证方式
EMQ X 通过插件提供了多种灵活的认证方式,你可以在这里找到 EMQ X 自带的插件列表,Maque IotHub 使用 MongoDB 作为数据存储,所以这里我们选择 MongoDB 认证插件。除了使用 MongoDB 认证以外,我们还会使用 JWT 的认证方式来提供一种临时性的接入认证。
在启用认证插件之前,我们需要关闭 EMQ X 的默认匿名认证。
编辑/etc/emqx.conf
,修改以下配置项:
allow_anonymous = false
然后重新启动 EMQ X/bin/emqx restart
。
MongoDB 认证
MongoDB 的认证插件功能逻辑很简单:将设备的 username、password 存储在 MongoDB 的某个 Collection 中,当设备发起 Connect 的时候,Broker 再查找这个 Collection,如果 username/password 能匹配得上,则允许连接,否则拒绝连接。
在/etc/plugins/emqx_auth_mongo.conf
可以对 MongoDB 认证进行配置,配置项很多,在这里我们看几个关键的配置项。
MongoDB 地址:auth.mongo.server = 127.0.0.1:27017
。用于认证的数据库:auth.mongo.database = mqtt
存储设备 username 和 password 的数据库,这里暂时用默认值。用于认证的 Collection:auth.mongo.auth_query.collection = mqtt_user
存储设备 username 和password 的 Collection, 这里暂时使用默认值。password 字段名:auth.mongo.auth_query.password_field = password
。password 加密方式:auth.mongo.auth_query.password_hash = plain
, password 字段的加密方式,这里选择不加密。是否打开超级用户查询:auth.mongo.super_query = off
,设置为关闭。是否打开权限查询:auth.mongo.acl_query = off
,这里我们暂时不打开 Publish 和 Subscribe 的权限控制。
然后我们在 MongoDB 插入一条记录,在 MongoDB Shell 中运行:
use mqttdb.createCollection("mqtt_user")db.mqtt_user.insert({username: "test", password: "123456"})
然后加载 MongoDB 认证插件:
/bin/emqx_ctl plugins load emqx_auth_mongo
不出意外的话控制台会输出:
Start apps: [emqx_auth_mongo]Plugin emqx_auth_mongo loaded successfully.
这个时候如果我们运行 test_mqtt.js,会得到以下输出:Error: Connection refused: Bad username or password
接下来,我们在连接的时候指定刚才存储在 MongoDB 的 username/password: test/123456
...var client = mqtt.connect('mqtt://127.0.0.1:1883', { username: "test", password: "123456"})...
重新运行 test_mqtt.js,如果
return code: 0
说明基于 MongoDB 的认证方式已经生效了。
如果返回的是Error: Connection refused: Bad username or password
,你需要检查:
插件的配置文件是否按照课程中的方式进行配置;MongoDB 插件是否成功加载,可通过运行/bin/emqx_ctl plugins load emqx_auth_mongo
查看;对应的设备数据是否添加到 MongoDB 对应的 collection 中。
可以通过运行/bin/emqx_ctl plugins list
方式查看插件列表,已加载的插件会显示 active=true。
JWT (JSON Web Token) 认证
JSON Web Token(JWT,读作 [/dʒɒt/]),是一种基于 JSON 的、用于在网络上声明某种主张的令牌(Token),更详细的介绍可以参考 jwt.io
EMQ X 提供了 JWT 认证插件来提供 JWT 方式的认证,在/etc/plugins/emqx_auth_mongo.conf
可以对 JWT 认证插件进行配置:
JWT Secret:auth.jwt.secret = emqxsecret
, 这里我们使用默认值,当然在实际生产中你需要使用一个长且复杂的字符串。是否开启 Claim 验证:auth.jwt.verify_claims = on
打开 Claim 验证。Claim 验证字段:auth.jwt.verify_claims.username = %u
需要验证 Claim 中的 username 字段。
下面我们来看下如何使用 JWT 来接入 Maque IotHub:
...var jwt = require('jsonwebtoken')var password = jwt.sign({ username: "jwt_user", exp: Math.floor(Date.now() / 1000) + 10}, "emqxsecret")var client = mqtt.connect('mqtt://127.0.0.1:1883', { username: "jwt_user", password: password})...
在这里我们使用 EMQ X 预设的 JWT Secret 签发了一个有效期为 10 秒的 JWT token 进行连接,重新运行 test_mqtt.js,如果输出为:
说明基于 JWT 的认证方式已经生效了。
插件的配置文件是否按照课程中的方式进行配置;JWT插件是否成功加载,可通过运行/bin/emqx_ctl plugins list
查看;是否是使用课程中指定的 payload 来生成 JWT 的。认证链
我们加载了 MongoDB 和 JWT 两个认证插件,EMQ X 就可以用这两个插件组成的认证链来对接入的 Client 进行认证。简单来说,设备既可以使用存储在 MongoDB 里的 username 和 password,也可以使用 JWT 来接入 EMQ X Broker。
EMQ X 在加载一个插件后,会把这个插件的名字写入/data/loaded_plugins
, EMQ X 在每次启动时都会自动加载这个文件里面包含的插件,所以我们只需要手动加载一次这两个插件就可以了。
我们在 MongoDB 里创建一个名为 IotHub 的数据来存储设备信息。
定义设备模型
这里,我们使用 mongoose 来做 MongoDB 相关的操作,首先定义 Device 模型:
// IotHub_Server/models/device.jsconst deviceSchema = new Schema({ //ProductName product_name: { type: String, required: true }, //DeviceName device_name: { type: String, required: true, }, //接入 EMQ X 时使用的 username broker_username: { type: String, required: true }, //secret secret: { type: String, required: true, }})
Restful API 实现
每次在生成新设备的时候,由系统自动生成 DeviceName 和 Secret,DeviceName 和 Secret 应该是随机且唯一的字符串,例如 UUID,这里,我们用 shortid 来生成稍短一点的随机唯一字符:
// routes/devices.js...router.post("/", function (req, res) { var productName = req.body.product_name var deviceName = shortid.generate(); var secret = shortid.generate(); var brokerUsername = `${productName}/${deviceName}` var device = new Device({ product_name: productName, device_name: deviceName, secret: secret, broker_username: brokerUsername }) device.save(function (err) { if(err){ res.status(500).send(err) }else{ res.json({product_name: productName, device_name: deviceName, secret: secret}) } })})...
接着我们将这个 router 挂载到 /devices 下面,并连接到 MongoDB:
//app.js...mongoose.connect('mongodb://iot:iot@localhost:27017/iothub', { useNewUrlParser: true })var deviceRouter = require('./routes/devices');app.use('/devices', deviceRouter);...
运行bin/www
启动 Web 服务器,然后在命令行用 curl 调用这个接口:
curl -d "product_name=IotApp" -X POST http://localhost:3000/devices
输出为:{"product_name":"IotApp","device_name":"V5MyuncRK","secret":"GNxU20VYTZ"}
ProductName 包含的字符是有限制的,不能包含· / +
以及 IotHub 预留的一些字符,为了演示,这里跳过了输入参数的校验,但是在实际项目中,是需要加上的。
修改 emqx_auth_mongo.conf
接下来需要按照我们定义的数据库结构来修改 EMQ X MongoDB 认证插件的配置,下面是需要在上一节内容上修改的项:
· 存储用户名和密码的 databaseauth.mongo.database = iothub· 存储用户名和密码的 collectionauth.mongo.auth_query.collection = devices· 密码字段auth.mongo.auth_query.password_field = secret· 查询记录时的 selectorauth.mongo.auth_query.selector = broker_username=%u
编辑完成以后重载下 MongDB 认证插件:/bin/emqx_ctl plugins reload emqx_auth_mongo
。
修改 DeviceSDK
接下在IoTHub_Device项目里对 DeviceSDK 进行修改,接受三元组作为初始化参数:
// sdk/iot_device.js...class IotDevice extends EventEmitter { constructor({serverAddress = "127.0.0.1:8883", productName, deviceName, secret} = {}) { super(); this.serverAddress = `mqtts://${serverAddress}` this.productName = productName this.deviceName = deviceName this.secret = secret this.username = `${this.productName}/${this.deviceName}` } connect() { this.client = mqtt.connect(this.serverAddress, { rejectUnauthorized: false username: this.username, password: this.secret }) ... } ...} ...
然后我们用刚才记录下的三元组作为参数调用 DeviceSDK 接入 Maque IotHub:
// samples/connect_to_server.js...var device = new IotDevice({productName: "IotApp", deviceName: "V5MyuncRK", secret: "GNxU20VYTZ"})...
然后再运行samples/connect_to_server.js
,会得到以下输出:
device is online
添加数据库索引
我们需要对 Devices 的 product_name 和 device_name 做一个索引,因为在后面会经常通过这两个字段对 devices 进行查询,在 MongoDB shell 里面输入:
use iothubdb.devices.createIndex({ "production_name" : 1, "device_name" : 1}, { unique: true })
MongoDB 插件在每次设备接入的时候都会使用 brokername 来查询 Devices Collectiion, 所以我们也需要在 brokername 上加一个索引:
use iothubdb.devices.createIndex({ "broker_username" : 1})
使用持久化连接
细心的读者可能已经发现了, DeviceSDK 在连接到 Broker 的时候并没有指定 Client Identifier。没错,到目前为止,我们使用的都是在连接时自动分配的 Client Identifer, 没有办法很好地使用 QoS1 和 QoS2 的消息。
Client Identifier 是用来唯一标识 MQTT Client 的,由于我们之前的设计保证了(ProductName, DeviceName)是全局唯一的,所以一般来说用这个二元组作为 Client Identifier 就足够了。 但是,之前我也提到过,在某些场景下,可能会出现多个设备使用同样的设备三元组接入 Maque IotHub,综合这些情况,我们这样来设计 Maque IotHub 里的 Client Identifier。
设备提供一个可选的 ClientID 来标识自己,可以是硬件编号、AndroidID 等,如果设备提供 ClientID,那么使用 ProductName/DeviceName/ClientID 作为连接 Broker 的Client Identifier,否则使用ProductName/DeviceName。 根据这个规则对 DeviceSDK 进行修改。
// IotHub_Device/sdk/iot_devices.js...class IotDevice extends EventEmitter { constructor({serverAddress = "127.0.0.1:8883", productName, deviceName, secret, clientID} = {}) { super(); this.serverAddress = `mqtts://${serverAddress}` this.productName = productName this.deviceName = deviceName this.secret = secret this.username = `${this.productName}/${this.deviceName}` //根据 ClientID 设置 if(clientID != null){ this.clientIdentifier = `${this.username}/${clientID}` }else{ this.clientIdentifier = this.username } } connect() { this.client = mqtt.connect(this.serverAddress, { rejectUnauthorized: false, username: this.username, password: this.secret, //设置 ClientID 和 clean session clientId: this.clientIdentifier, clean: false }) ... } ...
之后你可以再运行一次samples/connect_to_server.js
看下效果。
Node.js 的 MQTT 库自带了断线重连功能,所以这里就不用我们来实现了。
更多的 Server API
获取某个设备的信息
当业务系统查询设备信息的时候,我们并不是把 Device 的所有字段都返回。首先定义下返回内容:
// IotHub_Server/models/device.js//定义 device.toJSONObjectdeviceSchema.methods.toJSONObject = function () { return { product_name: this.product_name, device_name: this.device_name, secret: this.secret }}
然后进行接口实现:
// IotHub_Server/routes/devices.jsrouter.get("/:productName/:deviceName", function (req, res) { var productName = req.params.productName var deviceName = req.params.deviceName Device.findOne({"product_name": productName, "device_name": deviceName}, function (err, device) { if (err) { res.send(err) } else { if (device != null) { res.json(device.toJSONObject()) } else { res.status(404).json({error: "Not Found"}) } } })})
curl http://localhost:3000/devices/IotApp/V5MyuncRK{"product_name":"IotApp","device_name":"V5MyuncRK","secret":"GNxU20VYTZ"}
列出某个产品下的所有设备// IotHub_Server/routes/devices.jsrouter.get("/:productName", function (req, res) { var productName = req.params.productName Device.find({"product_name": productName}, function (err, devices) { if (err) { res.send(err) } else { res.json(devices.map(function (device) { return device.toJSONObject() })) } })})
curl http://localhost:3000/devices/IotApp[{"product_name":"IotApp","device_name":"V5MyuncRK","secret":"GNxU20VYTZ"}]
获取接入 Broker 的一次性密码(JWT)// IotHub_Server/routes/tokens.jsvar express = require('express');var router = express.Router();var shortid = require("shortid")var jwt = require('jsonwebtoken')//这个值应该和 EMQ X etc/plugins/emqx_auth_jwt.conf 中的保存一致const jwtSecret = "emqxsecret"router.post("/", function (_, res) { var username = shortid.generate() var password = jwt.sign({ username: username, exp: Math.floor(Date.now() / 1000) + 10 * 60 }, jwtSecret) res.json({username: username, password: password})})module.exports = router
// IotHub_Server/app.jsvar tokensRouter = require('./routes/tokens')app.use('/tokens', tokensRouter)
通过这个接口,可以签发一个有效期为 1 分钟的 username/password:
curl http://localhost:3000/tokens -X POST{"username":"apmE_JPll","password":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFwbUVfSlBsbCIsImV4cCI6MTU2ODMxNjk2MSwiaWF0IjoxNTU4MzE2OTYxfQ.-SnqvBGdO3wjSu7IHR91Bo58gb-VLFuQ28BeN6hlTLk"}
大家可能还发现了,在 ServerAPI 里面没有对调用者的身份进行认证和权限控制,也没有对输入参数进行校验,输出列表时也没有进行分页等的处理,当然在实际的项目中,这些都是有必要的。 但是这些属于 Web 编程的范畴,我想大家应该都非常熟悉了,所以在本课程中就跳过了,让课程的内容紧贴主题。
从环境变量中读取配置
根据 The Twelve-Factor App 的理念,从环境变量中读取配置项是一个非常好的 Practice,在我们的项目中有两个地方要用到配置:
这里我们使用 dotenv 来管理环境变量,它可以从一个 .env 文件中读取并设置环境变量。
附录: 如何运行 Maque IotHub第 1-5 课:设备在线状态管理(一)第 1-6 课:设备在线状态管理(二)第 1-7 课:设备禁用与删除第 1-8 课:设备权限管理第 1-9 课:加一点扩展性第 2-1 课:选择一个可扩展的上行数据处理方案第 2-2 课:功能设计第 2-3 课:实现(一)第 2-4 课:实现(二)第 2-5 课:设备状态上报第 2-6 课:时序数据库第 3-1 课:选择下行数据处理方案第 3-2 课:功能设计第 3-3 课:设备端实现第 3-4 课:服务端实现(一)第 3-5 课:服务端实现(二)第 4-1 课:RPC 式调用(一)第 4-2 课:RPC 式调用(二)第 4-3 课:设备数据请求第 4-4 课:NTP 服务第 4-5 课:设备分组——功能设计第 4-6 课:设备分组——服务端实现第 4-7 课:设备分组——设备端实现第 4-8 课:设备间通信第 4-9 课:OTA 升级——功能设计第 4-10 课:OTA 升级——服务端实现第 4-11 课:OTA 升级——设备端实现第 4-12 课:设备影子概览第 4-13 课:设备影子——服务端实现第 4-14 课:设备影子——设备端实现第 4-15 课:IotHub 状态监控第 5-1 课:EMQ X 的插件系统第 5-2 课:我们会用到的 Erlang 特性第 5-3 课:搭建开发和编译环境第 5-4 课:编写 emqx-rabbitmq-hook(一)第 5-5 课:编写 emqx-rabbitmq-hook(二)第 5-6 课:使用 emqx-rabbitmq-hook第 6-1 课:CoAP 简介第 6-2 课:IotHub 接入 CoAP
Lesen Sie den vollständigen Text WEB网址:http://gitbook.cn/gitchat/column/5d3a7c335cb084142168b3fc
搭建一个物联网平台需要多少钱?
如果有人问:【我想做一个物联网平台。你认为这要花多少钱?】这个问题很难直接回答,但是真的不是不透明!以前我都是计算开发一个物联网平台要多少钱。上来就先说各种成本。今天我就来说说影响物联网报价的重要因素——客户的需求。
如果一个客户需要物联网的外包开发功能只是简单的产品展示,那么这一类的APP报价一般都比较低,都是在几万到十几万不等。如果物联网定制开发的功能非常复杂比如需要带有支付、身份认证、即时通讯、物流、定位、评价等功能,那么价格也会随之提高,从到十几万到几十万不等。
物联网的需求实在是太宽泛了,客户只有细化“我想做一个什么类型物联网,都想实现哪些功能”这些问题后,将需求交给开发公司,这样项目经理才能给出一个合理的报价评估。否则上来就给你报价没有任何意义,到头来还会导致纠纷。
需求定义不单单需要定义你的功能需求,还需要定义你的设计需求与性能需求。
设计需求主要为物联网展示的风格,一个好的设计师可以根据你的要求,结合自己的经验设计出你预期效果的界面,而一般的刚开始做设计的设计师可能根据你的要求开始无法达到你期望的效果,需要反复修改后才可能达到你的要求。要相信好的设计师虽然价格较高,但可以保证工期。
性能需求往往被人忽略,大多数只看功能是否实现,容易遗漏性能需求,例如:十个人支付购买和一千个人同时支付购买这就不是两个零上的问题了,当然性能需求的提高,价格与工期也会增长。
软件开发所有的报价都不是一个很标准的东西,就像我们自己家的装修一样。你把费用压得太死,别人过了z就要偷工减料了,所以在预算有限,价格降不下来的情况下,我们不妨换个角度,不选择降价,而是精简那些不必要的功能,保证产品的质量。
本段是额外嵌入说明:来自红匣子编辑,作者用心编辑不图回报,与作者交流请166 2051 1776(可微)交换意见。
开发一个物联网平台要多少钱?
开发一个物联网平台要多少钱?
2023-06-06 10:13 发布于:河北省
物联网开发技术是当今最热门的技术之一,在许多领域都有巨大的商业价值。随着物联网技术的迅速发展,这对企业来说是一个很好的机会,可以充分利用这些技术来提高其效率和生产力。
那么,开发物联网平台需要多少钱呢?答案是没有准确的答案,因为这取决于很多因素,包括物联网平台的类型、需求、客户服务以及开发团队所能提供的资源。下面将为您详细介绍开发物联网平台需要多少费用以及如何在不影响项目预算的情况下做出最好的选择。
确定物联网平台类型
每个企业都需要确定自己的物联网平台类型,这将有助于决定要使用哪种平台来满足自己的业务需求。不同的物联网平台可以提供不同的功能和服务。在选择物联网平台时,必须确定企业的具体需求,以确定所需的服务类型。
对于大型企业,物联网平台的投资可以扩展到非常复杂和大型的应用程序,从而实现更高级别的自动化或业务流程优化。对于小型企业和初创企业,物联网平台可以更好地满足其需求。因此,物联网平台类型也将影响开发费用。
确定您的需求
要想开发物联网平台,您需要知道您想要实现什么。如果您只是为了实现现有设备的数据收集功能,那么您可以考虑购买一套现成的解决方案。如果您是想为企业提供一种创新的物联网解决方案,那么您可以选择开发自己的解决方案。
开发物联网平台需要多少费用取决于你的目标和需求,这些决定了你将需要什么。例如,如果你希望构建一个连接智能家居设备的系统,那么你需要一个物联网平台,这样可以更轻松地管理和监控其设备,并且可以减少故障。如果您想在本地部署物联网平台,那么就需要一些额外的专业知识和技能来构建其软件。
选择合适的物联网平台
如果企业要开发一个物联网平台,那么需要根据自己的业务需求和项目规模来选择合适的物联网平台。如果项目规模较小,则可以考虑使用低功耗蓝牙或 Zigbee等技术;而如果项目规模较大,则可以使用物联网云平台。
实施物联网平台
在实施物联网开发平台之前,必须仔细考虑需要采取哪些步骤来完成其工作。这可以确保项目可以顺利进行,并最大限度地减少成本。可以采用以下步骤来帮助企业:
1.确定需要哪些设备;
2.配置和部署物联网解决方案;
3.管理物联网设备的状态并收集数据;
4.使用物联网设备进行管理,并通过应用程序与后台系统通信;
5.分析数据并与客户和利益相关者共享信息。
监控和维护
物联网平台的监控维护成本可能是任何项目的主要成本。如果需要监控设备,就需要购买昂贵的传感器。传感器本身的成本可能很高,但从长远来看,如果设备出了问题,传感器会为你节省大量的时间和金钱。维护费用取决于您想要监控的设备的类型、数量和工作范围。