This commit is contained in:
louzefeng
2024-07-09 18:38:56 +00:00
parent 8bafaef34d
commit bf99793fd0
6071 changed files with 1017944 additions and 0 deletions

View File

@@ -0,0 +1,376 @@
<audio id="audio" title="06 | 物模型:如何定义智能电灯?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/73/c8/735332f3f126dca317e3b65b62640bc8.mp3"></audio>
你好,我是郭朝斌。
在基础篇最后一讲的智能家居项目里,我们设计了几个小场景,其中就包括智能电灯。如果你只是想自娱自乐,做一个可以用手机 App 控制的电灯,那么只要通过代码实现控制功能就足够了。至于是怎么控制的,电灯有什么状态上报,你自己知道就行了。
但是,如果你想让智能电灯真正成为物联网系统的一部分,那就不仅仅是在封闭的、确定的场景下写几行代码的事儿了。在物联网平台上,可能有其他人开发的应用需要显示你的智能电灯的状态;也可能有别的设备,比如光照传感器、智能音箱,在场景联动中要控制灯的状态。
所以,你需要把控制电灯打开和关闭的方法,告诉这些应用和产品的开发人员。同时,这些开发人员也需要了解,智能电灯的状态信息如何获取和解析。那么,你面临的第一个问题就是,**用什么方式提供这些接口信息呢?**
另外,市面上不止一款智能电灯,如果要一一适配,那工作量肯定很大,而且扩展起来会很困难。那么,你面临的第二个问题就是,**平台应用如何避免针对每款智能灯进行定制开发呢?**
计算机领域的软件体系结构采用的是一种**层**的结构,所以有人说过这么一句名言:**“计算机科学领域的任何问题,都可以通过增加一个间接的中间层来解决。”**
按照这个思路,我们就可以在智能电灯实体和平台之间,增加一层标准规范来解决这些问题。就像,你使用不同的浏览器访问极客时间的网站,都可以看到课程的文本、音频、视频等内容,因为这些内容都是基于 **HTML** HyperText Markup Language超文本标记语言等规范组织的。
物联网中的这层规范就是 Thing Specification Language简称 **TSL**。使用 TSL 描述的物联网中的实体模型,就是**“物模型”**,或者叫做“产品模型”,也有叫“数据模板”的。不过,我认为“物模型”更有物联网专属的感觉,所以在咱们这门课里我都会用“物模型”这个叫法。
## 物模型和设备的关系是什么?
物模型是物理世界的实体东西的一个抽象,是进行数字化描述后,用于数字世界的数字模型。这么说可能有点绕,更直接一点说就是,物模型是使用计算机可以理解的语言,说清楚这个产品**是什么**、**能做什么事情**,以及**可以提供哪些信息**。
而抽象就是要提取出产品的共同特征,形成模型。以智能灯为例,不同的灯,尽管规格不同,但它们的属性是相似,比如都有开关状态的属性,功能逻辑也相仿。我们可以将这些特征标准化,形成智能灯的物模型。
反过来,物模型也规约了设备的功能。新增加的设备,如果是同一类型的,在设计、研发中,会遵循相同的功能定义,有相同的特征,实现相同的服务。比如,灯都应该有“开”和“关”两种状态。
<img src="https://static001.geekbang.org/resource/image/dd/d7/dd2054abb168001194e69873d9f7bbd7.jpg" alt="">
## 为什么要使用物模型?
基于共同的抽象特征,物模型可以让应用程序不再针对一个个的产品设备,而是同一类设备采用相同的处理逻辑。这实际上是**应用开发的基础**。当烟感传感器的数值触发报警时,即使是不同品牌的烟感产品,应用程序也可以对数值做相同的处理和判断,否则只能分别进行数值分析。
另外,物模型中,设备的功能是明确定义的,可以方便地实现场景联动。比如,光线传感器可以基于光照强度,向智能电灯发送亮度的控制命令,或者开和关的命令。
## 如何定义物模型?
那么,如何定义智能电灯的物模型呢?这里我想告诉你结论,我们一般是通过属性、事件和动作这三种功能元素来定义。接下来,我就一一和你介绍。
我们知道,智能电灯的状态,要么是打开,要么是关闭;当进行控制时,这两种状态还会相互转换。此外,有些灯还可以根据需求设置不同的亮度、颜色和色温等。
它们的共同点就是,都描述了产品设备运行时的某种状态,我们用**属性Property**来表示。
属性的特点是可读可写。也就是说,应用程序可以读取属性,也可以设置设备的属性。我们还可以看到类似的例子,比如环境监测设备的温度、湿度这两个属性等。
如果智能电灯在运行过程中,出现了低电压的情况,或者发生了硬件故障,那么联网的设备可以将这些信息发送出去,通知你来及时作出处理。
这类由产品设备在运行过程中产生的信息、告警和故障等,就是**事件Event**。
一个事件可以包含多个输出参数。事件不同于属性,事件是设备上报的,不能由应用来设置。类似的例子,还有某任务完成时的消息,环境传感器检测到污染物的告警等。
我们再看生活中关于灯的一个使用场景:第一次约会的时候,你希望灯能够烘托出浪漫的气氛,就要调节灯的颜色、亮度和色温。如果分别设置属性,将会非常繁琐,这时你会想到要为灯增加一个场景模式的功能,一个命令就可以设置到浪漫模式。
这种设备可以被调用的能力或者方法,就是**动作Action**,也被称作**服务Service**。
动作由应用下发给设备,设备可以返回结果给应用。从执行的流程看,动作还可以进一步分为同步和异步。这取决于动作是否是个耗时的操作,以及其他应用逻辑对于动作执行结果的依赖关系。
你可能想,设置属性也可以改变设备的状态,那它们的区别是什么呢?相比于属性,动作是应用下发到设备的控制命令;动作可通过一条指令实现更复杂的业务逻辑,比如,调低温度 5 度,旋转摄像头 30°等。
<img src="https://static001.geekbang.org/resource/image/fc/e7/fc9a7eef37e61930d6f8fd761c5f97e7.jpg" alt="">
到这里,我们定义了属性、事件和动作这三类功能,也就完成了物模型的定义。
接下来,我们要做的是通过数据来描述它们。和编程语言一样,作为一种模型语言,物模型的数据也有不同的数据类型。它们主要包括六种:
1. **布尔型**Bool非真即假的二值型变量。例如开关功能只有开、关两种状态。
1. **整数型**Int可用于线性调节的整数变量。例如电灯的亮度是一个整数范围。
1. **字符<strong><strong>串**</strong></strong>String以字符串形式表达的功能点。例如灯的位置。
1. **浮点型**Float精度为浮点型的功能点。例如电压值的范围是0.0 - 24.0。
1. **枚举型**Enum自定义的有限集合值。例如灯的颜色有白色、红色、黄色等。
1. **时间型**TimestampString 类型的 UTC 时间戳。
对于整数型、浮点型的数值,它们的单位可以是百分比、电压、米等。
物模型一般是用 **JSON 格式**来表述模型元素。JSON 是 Web 开发中,经常使用的数据格式,相比于 XML它更加简洁、清晰也更轻量级。
在实践中,你手动写完 JSON 格式的物模型后,可以使用检测工具来验证语法是否正确,比如,在线检测工具 [JSON Schema Lint](https://jsonschemalint.com/)。
接下来,我们就按照属性、事件、动作/服务这三个要素一起看看如何用JSON格式来定义智能电灯的物模型吧。
## 定义智能电灯的物模型
智能电灯的开关属性是布尔类型,是必须有的属性。它可以通过 JSON 表述如下:
```
{
&quot;id&quot;: &quot;power_switch&quot;, //属性的唯一标识
&quot;name&quot;: &quot;电灯开关&quot;, //名称
&quot;desc&quot;: &quot;控制电灯开灭&quot;, //属性的详细描述
&quot;required&quot;: true, //表示此属性是否必需包含,是
&quot;mode&quot;: &quot;rw&quot;, //属性的模式r代表读w代表写
&quot;define&quot;: { //属性的数值定义
&quot;type&quot;: &quot;bool&quot;, //数值的类型,布尔
&quot;mapping&quot;: { //具体数值的含义
&quot;0&quot;: &quot;关&quot;, //0表示灯关闭
&quot;1&quot;: &quot;开&quot; //1表示灯打开
}
}
}
```
智能电灯的电压是需要监控的数值当电压低时可以上报这个事件。这个事件有一个参数即电压值数据类型是浮点类型。JSON 格式的描述如下:
```
{
&quot;id&quot;: &quot;low_voltage&quot;, //事件唯一标识
&quot;name&quot;: &quot;LowVoltage&quot;, //事件名称
&quot;desc&quot;: &quot;Alert for device voltage is low&quot;, //事件的描述
&quot;type&quot;: &quot;alert&quot;, //事件的类型,告警
&quot;required&quot;: false, //表示此属性是否必需包含,否
&quot;params&quot;: [ //事件的参数
{
&quot;id&quot;: &quot;voltage&quot;, //事件参数的唯一标识
&quot;name&quot;: &quot;Voltage&quot;, //事件参数的名称
&quot;desc&quot;: &quot;Current voltage&quot;, //参数的描述
&quot;define&quot;: { //参数的数值定义
&quot;type&quot;: &quot;float&quot;, //数值类型,浮点数
&quot;unit&quot;: &quot;V&quot;, //数值的单位,伏
&quot;step&quot;: &quot;1&quot;, //数值变化的步长1
&quot;min&quot;: &quot;0.0&quot;, //数值的最小值
&quot;max&quot;: &quot;24.0&quot;, //数值的最大值
&quot;start&quot;: &quot;1&quot; //事件的起始值
}
}
]
}
```
动作的定义和属性、事件的定义过程类似这里我就不再单独解释了。我们直接将所有属性、事件和动作合并就得到了智能电灯物模型的完整JSON格式
```
{
&quot;version&quot;: &quot;1.0&quot;, //模型版本
&quot;properties&quot;: [ //属性列表
{
&quot;id&quot;: &quot;power_switch&quot;, //电灯开关属性
&quot;name&quot;: &quot;电灯开关&quot;,
&quot;desc&quot;: &quot;控制电灯开灭&quot;,
&quot;required&quot;: true,
&quot;mode&quot;: &quot;rw&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;bool&quot;,
&quot;mapping&quot;: {
&quot;0&quot;: &quot;关&quot;,
&quot;1&quot;: &quot;开&quot;
}
}
},
{
&quot;id&quot;: &quot;brightness&quot;, //亮度属性
&quot;name&quot;: &quot;亮度&quot;,
&quot;desc&quot;: &quot;灯光亮度&quot;,
&quot;mode&quot;: &quot;rw&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;int&quot;,
&quot;unit&quot;: &quot;%&quot;,
&quot;step&quot;: &quot;1&quot;,
&quot;min&quot;: &quot;0&quot;,
&quot;max&quot;: &quot;100&quot;,
&quot;start&quot;: &quot;1&quot;
}
},
{
&quot;id&quot;: &quot;color&quot;, //电灯颜色属性
&quot;name&quot;: &quot;颜色&quot;,
&quot;desc&quot;: &quot;灯光颜色&quot;,
&quot;mode&quot;: &quot;rw&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;enum&quot;,
&quot;mapping&quot;: {
&quot;0&quot;: &quot;Red&quot;,
&quot;1&quot;: &quot;Green&quot;,
&quot;2&quot;: &quot;Blue&quot;
}
}
},
{
&quot;id&quot;: &quot;color_temp&quot;, //色温属性
&quot;name&quot;: &quot;色温&quot;,
&quot;desc&quot;: &quot;灯光冷暖&quot;,
&quot;mode&quot;: &quot;rw&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;int&quot;,
&quot;min&quot;: &quot;0&quot;,
&quot;max&quot;: &quot;100&quot;,
&quot;start&quot;: &quot;0&quot;,
&quot;step&quot;: &quot;10&quot;,
&quot;unit&quot;: &quot;%&quot;
}
}
],
&quot;events&quot;: [ //事件列表
{
&quot;id&quot;: &quot;status_report&quot;, //运行状态报告
&quot;name&quot;: &quot;DeviceStatus&quot;,
&quot;desc&quot;: &quot;Report the device status&quot;,
&quot;type&quot;: &quot;info&quot;,
&quot;required&quot;: false,
&quot;params&quot;: [ //事件参数列表
{
&quot;id&quot;: &quot;status&quot;,
&quot;name&quot;: &quot;running_state&quot;,
&quot;desc&quot;: &quot;Report current device running state&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;bool&quot;,
&quot;mapping&quot;: {
&quot;0&quot;: &quot;normal&quot;,
&quot;1&quot;: &quot;fault&quot;
}
}
},
{
&quot;id&quot;: &quot;message&quot;,
&quot;name&quot;: &quot;Message&quot;,
&quot;desc&quot;: &quot;Some extra message&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;string&quot;,
&quot;min&quot;: &quot;0&quot;,
&quot;max&quot;: &quot;64&quot;
}
}
]
},
{
&quot;id&quot;: &quot;low_voltage&quot;, //低电压告警事件
&quot;name&quot;: &quot;LowVoltage&quot;,
&quot;desc&quot;: &quot;Alert for device voltage is low&quot;,
&quot;type&quot;: &quot;alert&quot;,
&quot;required&quot;: false,
&quot;params&quot;: [
{
&quot;id&quot;: &quot;voltage&quot;,
&quot;name&quot;: &quot;Voltage&quot;,
&quot;desc&quot;: &quot;Current voltage&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;float&quot;,
&quot;unit&quot;: &quot;V&quot;,
&quot;step&quot;: &quot;1&quot;,
&quot;min&quot;: &quot;0.0&quot;,
&quot;max&quot;: &quot;24.0&quot;,
&quot;start&quot;: &quot;1&quot;
}
}
]
},
{
&quot;id&quot;: &quot;hardware_fault&quot;, //硬件错误事件
&quot;name&quot;: &quot;Hardware_fault&quot;,
&quot;desc&quot;: &quot;Report hardware fault&quot;,
&quot;type&quot;: &quot;fault&quot;,
&quot;required&quot;: false,
&quot;params&quot;: [
{
&quot;id&quot;: &quot;name&quot;,
&quot;name&quot;: &quot;Name&quot;,
&quot;desc&quot;: &quot;Name like: memory,tf card, censors ...&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;string&quot;,
&quot;min&quot;: &quot;0&quot;,
&quot;max&quot;: &quot;64&quot;
}
},
{
&quot;id&quot;: &quot;error_code&quot;,
&quot;name&quot;: &quot;Error_Code&quot;,
&quot;desc&quot;: &quot;Error code for fault&quot;,
&quot;define&quot;: {
&quot;type&quot;: &quot;int&quot;,
&quot;unit&quot;: &quot;&quot;,
&quot;step&quot;: &quot;1&quot;,
&quot;min&quot;: &quot;0&quot;,
&quot;max&quot;: &quot;2000&quot;,
&quot;start&quot;: &quot;1&quot;
}
}
]
}
],
&quot;actions&quot;: [], //动作列表
&quot;profile&quot;: { //产品参数
&quot;ProductId&quot;: &quot;8D1GQLE4VA&quot;, //产品ID
&quot;CategoryId&quot;: &quot;141&quot; //产品分类编号
}
}
```
## 每个模型都要从头定义吗?
那我们在创建自己的新模型时,是不是每次都需要从头定义这些属性、事件和动作呢?有没有更简便的方式呢?答案当然是有的。
创建模型的时候,有拷贝和继承两种模式,这两种创建模式的不同主要体现在**模型关系**上。
**“拷贝”模式**类似于编程语言中的值拷贝,新建模型与被拷贝模型有完全相同的三元素,两个模型相互独立,模型变更互不影响。
**“继承”模式**就是面向对象编程中的继承概念,新建模型被定义为“子模型”,被继承的模型定义为“父模型”。
继承的具体特征是:
1. 子模型继承父模型的所有要素,且继承的元素无法被修改。
1. 子模型可以再被继承,支持多层的继承关系。
1. 子模型可以创建独立的要素,但子模型中新增的要素不可以和所有上级父模型中的元素重名。
1. 当父模型中的元素发生变更时,子模型中继承自父模型的元素同步变更,保持与父模型一致。
以我们刚刚定义的智能电灯的物模型为例,如果要增加安装位置的属性,可以继承已有的模型,然后再增加安装位置的属性。(注意:下面的 JSON 表述省略了与父模型重复的内容。)
```
{
...
{
&quot;id&quot;: &quot;name&quot;, //灯位置属性
&quot;name&quot;: &quot;灯位置名称&quot;,
&quot;desc&quot;: &quot;灯位置名称:书房、客厅等&quot;,
&quot;mode&quot;: &quot;rw&quot;,
&quot;required&quot;: false,
&quot;define&quot;: {
&quot;type&quot;: &quot;string&quot;,
&quot;min&quot;: &quot;0&quot;,
&quot;max&quot;: &quot;64&quot;
}
}
...
}
```
到这里,我们已经了解了物模型,并且完整实践了一遍物模型的创建。接下来,我给你延伸一下,讲两个和物模型相关的概念。
## 物模型的拓展应用
你也许听到过“设备影子”和“数字孪生”这两个概念,它们和我们这里说的“物模型”是什么关系呢?
### 设备影子
设备影子用于**缓存设备状态**。应用程序可以通过设备影子直接获取设备最后一次更新的属性值,而无需每次都访问设备。设备在线时,可以直接获取应用指令;设备离线后,再次上线可以主动拉取应用指令。
我们可以再想象一个场景。如果设备网络稳定,很多应用程序请求获取设备状态,设备需要根据请求响应多次,即使响应的结果是一样的。但是可能设备本身处理能力有限,其实无法负载被请求多次的情况。
使用设备影子机制,设备只需要主动同步状态给设备影子一次,多个应用程序请求设备影子获取设备状态,即可获取设备最新状态,做到应用程序和设备的解耦。
再比如,智能电灯的开关状态这个属性,手机 App 可以远程控制,你也可以在本地通过物理开关改变。如果网络不稳定,那么平台上存储的状态,和电灯设备的真实状态可能会不一致,导致后续操作逻辑错误。
设备影子可以通过双向的同步,实现服务器端和设备端属性的一致,从而解决这个问题。
### 数字孪生Digital Twin
物模型是物理实体的数字化模型,但主要针对的是物联网中应用的开发和设备的互操作。
这个模型如果更进一步,集成了物理实体的各类数据,那就是物理实体的**忠实映射**。同时,在物理实体的整个生命周期中,它会和实体一起进化,积累各种信息和知识,并且促进物理实体的优化。这样的模型就是物理实体的数字孪生。
在工业物联网领域,这个概念已经有了很多的探讨和应用。
比如,特斯拉公司为其生产的每一辆电动汽车都建立了数字孪生模型,相关的模型数据保存在公司的数据库中,以便在测试中排查故障,为用户提供更好的服务。
## 小结
总结一下,在这一讲中,我通过智能电灯的例子讲解了物模型。
1. 物模型是物理世界中产品设备的数字化模型,它对设备的共同特征进行了抽象,同时规约了设备的设计。
1. 物模型一般是使用 TSL 描述的 JSON 格式文件。
1. 物模型包括属性、事件和动作三个功能元素。其中,属性可读可写;事件可以包括多个参数;动作包括应用下发的命令,和设备返回的响应信息
在实践中,定义物模型时,你需要注意物模型三个功能元素的区别,尤其要了解属性和动作的联系和不同。不好的定义会给功能实现带来困难,比如,将智能电灯的“开”和“关”,定义为两个不同的动作。
物模型在物联网系统开发中,作用重大,它为应用开发提供了统一的数据模板,方便了场景联动的实现,同时,为平台上实现设备影子提供了基础。
类似地,数字孪生也正是建立在物理实体的数字模型之上的重要技术方向。这里作为一个引子,有兴趣的话,你可以深入了解一下,也许对你在工作中做系统设计有帮助。
## 思考题
最后,我还是给你留个思考题作为结尾。
物模型是实战开发的基础,咱们最后再通过一个练习来强化下学习效果吧。请你定义一个环境温湿度传感器的物模型。你可以从属性、事件、动作三个元素的角度思考一下,而且一定要动手写一写。
欢迎在留言区写出你的答案,和我一起交流一下。在后面的实战中,我们也会涉及到温湿度传感器的物模型。如果你的朋友对物联网有兴趣,也欢迎你将这个课程分享给他们一起学习进步。

View File

@@ -0,0 +1,158 @@
<audio id="audio" title="07 | 零配置组网:设备如何发现彼此?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a5/dc/a5b4820231d7cae4cefa95eb224a09dc.mp3"></audio>
你好,我是郭朝斌。
不知道你还记不记得,在基础篇的[第2讲](https://time.geekbang.org/column/article/306976)中,我介绍了 Wi-Fi 设备的配网方式比如一键配网技术Smart Config和设备热点配网技术。这些技术已经可以实现**一定程度的自动化**让设备比较方便地连接上Wi-Fi 热点。
同时我也提到了零配置配网方式它试图通过已连接上Wi-Fi热点的设备来实现可信任设备**完全自动化**的配网。
但是,你可不要把**零配置配网**Zero Configuration Provisioning和我们这一讲要谈的**零配置组网**Zero Configuration Networking ZEROCONF混淆了。配网只是第一步因为物联网设备无法方便地输入信息所以相比手机或电脑我们引入了这个额外的操作步骤。
配网成功之后,我们还需要组网,也就是让设备获得一个**自己的IP地址**,同时也知道局域网内的**路由器**Router的IP地址和**DNS**Domain Name System的IP地址等信息。设备自己的IP地址是它在TCP/IP 网络中的唯一标识路由器可以把设备的数据包正确地转发出去而DNS服务器可以帮忙解析出数据包中需要设置的目的地 IP 地址。
>
温馨提示如果你对IP地址和DNS这样的基本概念还不是很熟可以学习[《趣谈网络协议》](https://time.geekbang.org/column/intro/100007101)等课程临时恶补一下。因为这一讲还会涉及不少网络基础知识,对它们有一定了解的话,你的学习效果会更好。
零配置组网就是把这些工作自动化,不需要用户手动去操作,甚至可以让这个设备与网络内的其他设备配合工作。
比如你要为自己的智能家居系统加一个投屏设备它可以外接普通电视或者显示器让你可以将手机或者电脑的屏幕在大屏上显示出来。你希望这个投屏设备接入Wi-Fi热点之后你就可以在手机上直接发现它并且马上开始投屏看自己想看的电影。
这个想法非常美,但是你知道要怎么实现吗?其实并不难,你只要掌握我在这一讲介绍的零配置组网就可以了。
### 设备如何获取 IP 地址?
零配置组网的第一步就是自动分配IP地址。
不知道你以前有没有这样的经验,在学校宿舍或者公司,当你的电脑通过网线连接网络时(现在用网线连网的方式已经不那么常见了),有时电脑桌面右下角会弹出气泡,提示“ IP 地址冲突”。
这是因为你的电脑的IP地址被网络内其他电脑占用了。所以你就需要打开网络配置的界面手动填写一个不同的 IP 地址。对于非专业人士来说,这并不是一个很简单的事情;而且对于特殊的**子网掩码**比如不像“255.255.25**5**.0”这样整齐而是“255.255.25**3**.0”这种),即使有经验的用户,可能也很难确定**子网号**和**主机号**分别是什么。
现在随着Wi-Fi的普及我们连网的时候基本上不会再碰到这种情况了。这一方面是因为每一个 Wi-Fi 热点接入的设备数量是有限的,另一方面则是因为 Wi-Fi 路由器提供了完善的 **IP 地址自动分配功能**
#### DHCP 协议
这个自动分配功能是基于 **DHCP 协议**Dynamic Host Configuration Protocol动态主机配置协议实现的。DHCP 协议的前身是 **BOOTP 协议**Bootstrap Protocol从“Bootstrap”这个名字你就可以看出来它解决的正是一个设备接入 IP 网络后,需要完成的第一件事——获取 IP 地址。
在网络发展的初期,接入网络的设备很少,所以,网络地址的分配和设置都是管理员手动来完成的。你可能想象不到,最初整个互联网的 DNS 数据库都是手动维护和更新的。
但是随着接入网络的设备越来越多而且设备更加便携经常移动来移动去手动的方式就跟不上时代发展了。DHCP 协议正是在这种背景下出现的。
也许你对DHCP的名字很熟悉但我还是想在这里稍微展开一下带你了解它背后的工作原理这样你就能在开发中更好地使用它。
DHCP 使用了服务器-客户端的架构模型。
- 当一个设备你的手机接入网络时它自己就会作为DHCP 客户端,请求网络地址。
- 然后DHCP 服务器家里的Wi-Fi路由器会从地址池中挑选一个IP地址分配给这个设备。
- 当设备不再使用这个 IP 时(你带着手机出门/睡觉飞行模式DHCP 服务器会进行回收,之后再分配给其他有需要的设备(你新买的平板)使用。
DHCP 服务器与设备之间的通信是通过 **UDP 传输协议**完成的。因为 UDP 有一个优势那就是不需要提前建立连接关系。DHCP 服务器的端口号是 67设备的端口号是 68它们一般的交互过程是这样的
<img src="https://static001.geekbang.org/resource/image/5a/aa/5a1b86b9a70504521010262148e761aa.jpg" alt="">
1. **Discover**:设备以广播的方式发送 DHCP Discover 消息,表示需要获取 IP 地址。
1. **Offer**DHCP 服务器收到这个消息后,会发出 DHCP Offer 消息,作为回应。消息中带有 DHCP 服务器为设备分配的 IP 地址,也会包含其自身的 IP 地址。
1. **Request**:设备收到 DHCP Offer 消息后,将会广播一条 DHCP Request 消息,正式向 DHCP 服务器请求这个 IP 地址。
1. **ACK**DHCP 服务器收到 DHCP Request 消息后,会判断服务器 IP 是否和自己的地址一致。如果一致,马上向设备回复 DHCP ACK 消息,并指定好 IP 地址和它的租用期限。
1. **Decline**:设备收到 DHCP ACK 消息后,还会验证一下 IP 地址是否可用。如果地址冲突,就说明不可用,它会发出 DHCP Decline 消息;如果地址不冲突,就可用的,设备将会按照租期使用这个 IP 地址。
1. **Release**:当设备不使用这个 IP 地址时,设备可以通过发送 DHCP Release 消息,来释放它。这样 DHCP 服务器可以重新分配这个 IP 地址。
### 如何让手机自动发现投影仪呢?
借助DHCP协议当你的投屏设备接入Wi-Fi热点后就可以自动获得一个自己的IP地址比如“192.168.1.100”同时还会自动得到路由器Router的IP地址并且完成DNS的IP地址的自动设置。
那接下来,怎么让手机自动发现它,然后直接使用呢?
不知道你留意过没有,如果你新买了一台打印机,当这台打印机连上家里的 Wi-Fi 之后,你在电脑上打印文件时,它会自动显示出来供你选择。这是怎么实现的呢?
分析一下一定是电脑通过某种方式知道有这台设备并且知道它就是打印机可以提供打印服务。打印机被电脑自动识别的这个过程就是借助UPnP协议完成的。
#### UPnP 协议
UPnP 是 Universal Plug and Play 的简称,它要实现的目标就是网络设备的即插即用。
UPnP 由设备寻址、设备发现、设备描述、设备控制、事件通知和基于 HTML 的描述界面六部分构成。其中设备寻址同样是基于我刚介绍过的 DHCP 实现如果网络内没有DHCP服务器UPnP会基于自己的AutoIP方法指定一个IP地址。
从整体看UPnP 是一个**多层协议构成的框架体系**,每一层都以相邻的下层为基础,同时又是相邻上层的基础,直至达到应用层为止。你可以参考下面的图片。
<img src="https://static001.geekbang.org/resource/image/99/a6/99edd1ea26dcd96eb73c6bb4acde4da6.jpg" alt="">
这里我重点介绍一下第三层(从下往上数),它基于 HTTP、HTTPU、HTTPMU协议属于传送协议层。传送的内容都是经过“封装”之后存放在特定的XML文件中的。用于**设备和服务发现**的SSDPSimple Service Discovery Protocol简单服务发现协议协议就是基于XML 传送数据的。
你不要被SSDP协议的名字迷惑了其实它既提供了服务发现的功能也提供了设备发现的功能。我们可以基于 SSDP中的M-SEARCH方法来查询设备然后基于设备的响应获得设备的服务能力的描述信息。同时设备可以通过NOTIFY 方法向网络通知自己的服务能力。
借助UPnP协议你的设备就可以自动被发现和使用了。这个自动化的过程我们通常用一个专有名词来概括也就是零配置组网。梳理一下零配置组网包括三个方面的技术内核
1. 为网络设备自动分配 IP 地址一般涉及DHCP协议和AutoIP方法
1. 自动发现和解析设备主要是基于SSDP协议
1. 自动传播和发现各网络设备提供的服务主要也是基于SSDP协议。
这里我贴一下我家里小米电视盒子的一个 UPnP 的组播包的内容。它每3秒就有一包这样用户体验更好手机或者电脑投屏时更容易发现它。
```
NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=66
LOCATION: http://192.168.31.188:49152/description.xml
NT: urn:schemas-upnp-org:service:ConnectionManager:1
NTS: ssdp:alive
SERVER: Linux/3.14.29, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
USN: uuid:F7CA5454-3F48-4390-8009-483842e84c17::urn:schemas-upnp-org:service:ConnectionManager:1
```
你可以发现它是基于HTTP1.1版本,准确地说,是 HTTPMU协议同时你可以看到组播地址端口号是1900。
那为了实现UPnP协议你可以使用哪些开源的代码实现呢
比较流行的开源库是 [libupnp](https://pupnp.sourceforge.io/),小米电视盒子使用的就是这个开源库。另外还有一个选择是[GUPnP项目](https://wiki.gnome.org/Projects/GUPnP)它包括几个不同的子项目比如实现SSDP协议的[GSSDP项目](https://gitlab.gnome.org/GNOME/gssdp/)。
Android平台上有一个开源库[Cling](https://github.com/4thline/cling),它是[4th Line组织](http://fourthline.org/)开发的,你可以参考。现在也只能说“参考”,使用的话,需要谨慎评估,因为它已经不再被维护。
#### mDNS 和 DNS-SD
除了 UPnP 协议,零配置组网还可以使用别的协议标准,比如 mDNS 和 DNS-SD 协议。
说起这两个协议,你可能比较陌生,不过苹果设备的 **AirDrop功能**你可能比较熟悉。AirDrop 是通过 **Bonjour 服务**来发现网络上的其他苹果设备的。这个 Bonjour 服务就是 mDNS 协议和 DNS-SD 协议的具体实现。
**mDNS** Multicast DNS协议允许设备在本地的 DNS 名字空间,设置一个本地的域名。之后被询问的时候,它就通过 UDP 把 IP 地址广播出来,这样其它的设备就可以找到它。你可以简单地把 mDNS 理解为 **DNS 的本地网络版本**
比如,主机 A 是 FTP 服务,它接入网络,并开启了 mDNS 服务,就会向 mDNS 服务注册服务信息:
我提供 FTP 服务我的IP是 192.168.1.101,端口是 21。
当主机 B 接入相同的网络时,如果它向主机 B 的 mDNS 服务请求,需要找局域网内 FTP 服务器,主机 B 的 mDNS 就会在局域网内向其他设备的 mDNS 询问。然后,它就会找到主机 A 的 IP 地址和端口号。
**DNS-SD**DNS Service Discovery 协议,一般是和 mDNS 一起使用的。它使用三种 DNS 协议的记录类型来定义协议内容三个记录分别是PTR 记录、SRV 记录和 TXT 记录。它提供了服务发现的功能,作用类似于上面讲到的 SSDP 协议。
### 小结
总结一下,在这一讲中,我介绍了零配置组网的相关技术。零配置组网是一个技术组合,它要实现的目的是自动化网络设备的初始配置过程,让用户不需要额外的手动操作。
1. 零配置组网的第一步是为网络设备自动分配 IP 地址。DHCP 协议是实现设备寻址的标准技术。它是工作在 UDP 协议之上的应用层协议,提供了完善的 IP 地址请求、IP地址分配和租期管理等功能。如果网络内没有DHCP服务器在UPnP中定义了AutoIP方法设备可以自己分配IP地址。在最新的UPnP规范中 AutoIP 切换到了 Link-Local Addressing规范。这类IP地址一般是“**169.254**.0.0/16”范围的地址。
1. 第二步是自动发现和解析设备的名称。在UPnP中一般是基于SSDP协议另一种替代的方案是 mDNS 协议。
1. 第三步是自动传播和发现各网络设备提供的服务一种选择是采用SSDP协议另一种方案是基于mDNS的DNS-SD协议。
UPnP协议整合了DHCP、AutoIP和SSDP等协议规范提供了一个完整的组网协议框架。UPnP 协议实现的功能非常完善比如基于SOAP 提供了设备控制的能力,但是也引来了很多安全隐患,这个在后面我还会介绍到。
我在这一讲介绍的方法主要关于Wi-Fi设备的。你可能想问那蓝牙和 ZigBee 设备怎么办呢其实关于蓝牙和ZigBee这类设备智能家居厂家一般都会制定自己的私有协议比如小米有**Mibeacon协议**,我会在实战篇再为你介绍。
除了这些私有协议,行业内也有一些标准组织主导的开放协议,比如 **AllJoyn 协议**。它为蓝牙和ZigBee设备提供了设备发现和控制的解决方案。现在AllJoyn 已经和 IoTivity 合并。这个合并是为了在物联网场景下,让零配置组网技术进一步发展。
具体来说因为物联网设备用了很多不同的通信技术所以AllJoyn提供了一个抽象层。它为底层网络协议栈定义了统一的接口使得软件工程师可以相对容易地添加和安装新的网络。
AllJoyn 还采用了一种易于理解的对象模型和远程方法调用RMI机制并且它重新实现了总线协议基于D-BUS规范和扩展D-BUS协议用来支持分布式设备。
因为相对来说AllJoyn协议的行业应用还处于早期我就不展开介绍了有这方面需求或者兴趣的话推荐你可以到它的[官网](https://openconnectivity.org/technology/reference-implementation/alljoyn/)详细了解。
我这里总结了一个思维导图,供你参考。
<img src="https://static001.geekbang.org/resource/image/6e/7b/6e34893e226d5de8e5a5aa6d5059437b.jpg" alt="">
### 思考题
最后,我还是给你留一个思考题吧。
在 DHCP 协议的流程介绍中,我提到当设备收到 DHCP 服务器响应的 DHCP ACK 消息后,并不会立即使用这个 IP 地址,而是要先检测一下 IP 地址是否有效。那设备是通过什么方式检测 IP 地址的有效性的呢?
你可以在留言区和我交流你的思考。同时,也欢迎你将这一讲分享给你的朋友一起交流学习。

View File

@@ -0,0 +1,230 @@
<audio id="audio" title="08 | MQTT在实践中掌握一个通信协议" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/5b/ed/5bd624a3ec52f8d5f1da1bc68a4da3ed.mp3"></audio>
你好,我是郭朝斌。
在基础篇的[第 3 讲](https://time.geekbang.org/column/article/307518)中,我介绍了几种物联网系统中常用的网络协议。它们是物联网设备之间沟通的“语言”,使用同一种语言,双方才能通信成功。
那么在这么多网络协议当中最流行的是哪一种呢毫无疑问是MQTT协议它甚至已经成为物联网系统事实上的网络协议标准。如果你想从事物联网开发就一定要掌握MQTT。
所以这一讲我就会带你了解MQTT在实践中熟悉它的特性。
## 体验MQTT
为了让你对MQTT有一个直观的印象我先带你体验一下它的通信过程
第一步是安装 hbmqtt它是一个开源的基于Python语言的 MQTT Broker 软件,正好包括我们需要使用一些工具。跟其他选择相比,这个软件安装起来非常方便,因为它在 Python 的 PYPI 软件仓库中就有,所以你通过 pip 命令就可以安装。这也是选择使用它的主要原因。
不过要注意的是hbmqtt 是基于Python3实现的因此这里使用的是 pip3 工具。
```
pip3 install hbmqtt
```
安装完成后,我们就可以使用 hbmqtt 中提供的 hbmqtt_sub 和 hbmqtt_pub 这两个命令行工具了。通过名字,你应该也可以看出 hbmqtt_sub 可以充当**订阅者**的角色hbmqtt_pub 可以作为消息的**发布者**。
至于订阅者和发布者之间的经纪人,也就是 MQTT Broker我们使用 Eclipse 免费开放的在线 [Broker 服务](https://mqtt.eclipse.org/)。打开链接,你可以看到关于端口的介绍信息,加密和非加密方式都支持,而且还有基于 Websocket 的实现,这对基于前端网页的应用来说是非常有利的。
我们先使用 1883端口的非加密方式然后为消息传输确定一个主题Topic。主题确定了消息的类别用于消息过滤。比如我们待会儿要测试的消息属于极客时间平台的物联网课程所以主题可以设为“/geektime/iot”。
```
/geektime/iot
```
接着,我们在电脑的终端界面输入下面的命令,就可以订阅这个主题消息:
```
hbmqtt_sub --url mqtt://mqtt.eclipse.org:1883 -t /geektime/iot
```
如果你想了解一些命令的执行细节,可以在上面的命令中加上 “-d” 参数。
现在,我们启动另外一个终端界面,通过 hbmqtt_pub 发布一个 “/geektime/iot” 主题的消息:
```
hbmqtt_pub --url mqtt://mqtt.eclipse.org:1883 -t /geektime/iot -m Hello,World!
```
通过 Eclipse 的开放 Broker 作为“经纪人”,消息被传输到了我们通过 hbmqtt_sub 运行的订阅者那里。下图是我的终端界面上运行的结果,一个完整的消息传输过程就这样完成了。
<img src="https://static001.geekbang.org/resource/image/9a/ef/9a4fe3966ac303d5defde1bd8b7yy4ef.png" alt="">
## MQTT 的生态很完善
初步体验之后不知道你是不是有这样的感觉原来使用MQTT也没有那么难啊
确实不难甚至可以说很简单这主要得益于MQTT 协议出现的时间很久远。当然,时间久远本身不是优势,能力强才是。这些长期的使用和积淀使得 MQTT 的生态非常完善而生态是技术标准能够主导行业的关键。所以你在使用MQTT的时候会觉得很方便可供挑选的方案有很多。
比如在上面的体验中,你要安装 MQTT可以直接找到 [hbmqtt](https://github.com/beerfactory/hbmqtt) 这样的项目拿来用。它的背后有 Eclipse 基金会的支持。
类似的 MQTT Broker 软件你还可以选择基于C语言的[Mosquitto](http://mosquitto.org/)基于Erlang语言的[VerneMQ](https://vernemq.com/)等。
至于 MQTT 的客户端Client实现也有成熟的 Python、C、Java 和 JavaScript 等各种编程语言的开源实现,供你参考、使用,比如[Eclipse Paho项目](http://www.eclipse.org/paho/)。
而且,还有很多商业公司在持续运营功能更丰富、支持更完备的商业版 Broker 实现,比如提供高并发能力的集群特性、方便拓展的插件机制等。这些会大大提高我们技术开发者的工作效率。比如中国一个团队开发、维护的 [EMQ X](https://www.emqx.io/),它已经完整地支持 MQTT5.0协议。
另外生态完善还有一个好处那就是作为开发者当你遇到难题时可以很方便地找到很多相关的资料就算资料解决不了问题你还可以去社区中提问寻求高手的帮助。这在实际工作中非常有用。顺便提一下除了老牌的Stack Overflow你还可以关注一下 GitHub 的 Issues 模块,因为在那里可以找到很多专家。
## MQTT自身的“基因”很强大
当然,阿里云、华为云、腾讯云和微软 Azure 这些大厂,之所以不约而同地选择 MQTT 协议作为物联网设备的“第一语言”不仅是因为MQTT的生态完善MQTT协议本身的优秀设计也是重要的因素。
它在设计上的优点体现在哪呢?我想主要有五个方面:
1. 契合物联网大部分应用场景的**发布-订阅模式**。
1. 能够满足物联网中资源受限设备需要的**轻量级特性**。
1. 时刻关注物联网设备**低功耗需求的优化设计**。
1. 针对物联网中多变的网络环境提供的**多种服务质量等级**。
1. 支持在物联网应用中越来越被重视的**数据安全**。
接下来,我分别为你剖析一下。
### 发布-订阅模式
刚才我们体验的通信过程,是一个发布者和一个订阅者的情况。在这之后,你可以再打开一个终端界面,重复和之前一样的命令,再启动一个订阅者。
```
hbmqtt_sub --url mqtt://mqtt.eclipse.org:1883 -t /geektime/iot
```
现在,这两个订阅者都订阅了“/geektime/iot” 主题的消息。
然后,你再次使用 hbmqtt_pub 发送消息,就可以看到两个订阅者都收到了同样的消息,这是**发布-订阅模式**的典型特征。
因为采用了发布-订阅模式MQTT协议具有很多优点比如能让一个传感器数据触发一系列动作网络不稳定造成的临时离线不会影响工作方便根据需求动态调整系统规模等。这使得它能满足绝大部分物联网场景的需求。
### 轻量级协议:减少传输数据量
MQTT 是一个**轻量级**的网络协议,这一点也是它在物联网系统中流行的重要原因。毕竟物联网中大量的都是计算资源有限、网络带宽低的设备。
这种“轻量级”体现在两个方面。一方面MQTT 消息采用**二进制的编码格式**,而不是 HTTP 协议那样的文本的表述方式。
这有什么好处呢?那就是可以充分利用字节位,协议头可以很紧凑,从而尽量**减少需要通过网络传输的数据量**。
比如,我们分析 HTTP 的一个请求抓包,它的消息内容是下面这样的(注意:空格和回车、换行符都是消息的组成部分):
```
GET /account HTTP/1.1 &lt;--注释HTTP请求行
Host: time.geekbang.com &lt;--注释以下为HTTP请求头部
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
&lt;--注释:这个空行是必须的,即使下面的请求体是空的
```
在HTTP协议传输的这段文本中每个字符都要占用 1 个字节。而如果使用MQTT协议一个字节就可以表示很多内容。下面的图片展示了 MQTT 的固定头的格式这个固定头只有2个字节
<img src="https://static001.geekbang.org/resource/image/e0/03/e03f1b94406b4cf33d55c9361925e203.jpg" alt="">
你可以看到,第一个字节分成了高 4 位47和低 4 位03低 4 位是数据包标识位其中的每一比特位又可以表示不同的含义高4位是不同数据包类型的标识位。
第二个字节表示数据包头部和消息体的字节共个数,其中最高位表示有没有第三字节存在,来和第二个字节一起表示字节共个数。
如果有第三个字节那它的最高位表示是否有第四个字节来和第二个字节、第三个字节一起表示字节总个数。依此类推还可能有第四个字节、第五个字节不过这个表示可变头部和消息体的字节个数的部分最多也只能到第五个字节所以可以表示的最大数据包长度有256MB。
比如,一个请求建立连接的 CONNECT 类型数据包头部需要14个字节发布消息的 PUBLISH 类型数据包头部只有24个字节。
轻量级的另一方面,体现在消息的具体**交互流程设计非常简单**所以MQTT的交互消息类型也非常少。为了方便后面的讲解我在这里整理了一个表格总结了 MQTT 不同的数据包类型的功能和发消息的流向。
从表格可以看出MQTT 3.1.1 版本一共定义了14种数据包的类型在第一个字节的高 4 位中分别对应从 1 到 14 的数值。
<img src="https://static001.geekbang.org/resource/image/5d/e4/5d22e10c5b00a10177d76055db93f3e4.jpg" alt="">
### 低功耗优化:节约电量和网络资源
除了让协议足够轻量MQTT协议还很注重**低功耗**的优化设计,这主要体现在对能耗和通信次数的优化。
比如MQTT 协议有一个 **Keepalive 机制**。它的作用是,在 Client 和 Broker 的连接中断时,让双方能及时发现,并重新建立 MQTT 连接,保证主题消息的可靠传输。
这个机制工作的原理是Client 和 Broker 都基于 Keepalive 确定的时间长度,来判断一段时间内是否有消息在双方之间传输。这个 Keepalive 时间长度是在Client建立连接时设置的如果超出这个时间长度双方没有收到新的数据包那么就判定连接断开。
虽然Keepalive要求一段时间内必须有数据包传输但实际情况是Client 和 Broker 不可能时时刻刻都在传输主题消息,这要怎么办呢?
MQTT 协议的解决方案是,定义了 PINGREQ 和 PINGRESP 这两种消息类型。它们都没有可变头部和消息体,也就是说都只有 2 个字节大小。Client 和 Broker 通过分别发送 PINGREQ 和 PINGRESP 消息,就能够满足 Keepalive 机制的要求。
我猜你也想到了,如果要一直这样“傻傻地”定期发送消息,那也太浪费电量和网络资源了。所以,如果在 Keepalive 时间长度内Client 和 Broker 之间有数据传输,那么 Keepalive 机制也会将其计算在内,这样就不需要再通过发送 PINGREQ 和 PINGRESP 消息来判断了。
除了 Keepalive 机制MQTT 5.0 中的**重复主题特性**也能帮助我们节省网络资源。
Client 在重复发送一个主题的消息时,可以从第二次开始,将主题名长度设置为 0这样 Broker 会自动按照上次的主题来处理消息。这种情况对传感器设备来说十分常见,所以这个特性在工作中很有实际意义。
### 3种QoS级别可靠通信
除了计算资源有限、网络带宽低,物联网设备还经常遇到网络环境不稳定的问题,尤其是在移动通信、卫星通信这样的场景下。比如共享单车,如果用户已经锁车的这个消息,不能可靠地上传到服务器,那么计费就会出现错误,结果引起用户的抱怨。这样怎么应对呢?
这个问题产生的背景就是不稳定的通信条件,所以 MQTT 协议设计了 3 种不同的 QoS Quality of Service服务质量级别。你可以根据场景灵活选择在不同环境下保证通信是可靠的。
这 3 种级别分别是:
1. QoS 0表示消息**最多**收到一次,即消息可能丢失,但是不会重复。
1. QoS 1表示消息**至少**收到一次,即消息保证送达,但是可能重复。
1. QoS 2表示消息**只会**收到一次,即消息有且只有一次。
<img src="https://static001.geekbang.org/resource/image/63/d1/63ced1e04d756yy9ae89c3c81c8ac9d1.jpg" alt="">
我用一张图展示了它们各自的特点。可以看到QoS 0 和 QoS 1 的流程相对比较简单;而 QoS 2 为了保证有且只有一次的可靠传输,流程相对复杂些。
正常情况下QoS 2有PUBLISH、PUBREC、PUBREL 和 PUBCOMP 4 次交互。
至于“不正常的情况”,发送方就需要重复发送消息。比如一段时间内没有收到 PUBREC 消息就需要再次发送PUBLISH消息。不过要注意这时要把消息中的 “重复”标识设置为1以便接收方能正确处理。同样地如果没有收到 PUBCOMP 消息发送方就需要再次发送PUBREL消息。
剖析到这里MQTT协议本身的主要特性我就介绍完了我们已经为在实战篇编写 MQTT 的相关通信代码做好了准备。但是,我还想跟你补充一个跟生产环境有关的知识点,那就是**数据安全传输**。
### 安全传输
说到安全传输首先我们需要验证Client是否有权限接入MQTT Broker。为了控制Client的接入MQTT 提供了**用户名/密码**的机制。在建立连接过程中,它可以通过判断用户名和密码的正确性,来筛选有效连接请求。
但是光靠这个机制,还不能保证网络通信过程中的数据安全。因为在明文传输的方式下,不止设备数据,甚至用户名和密码都可能被其他人从网络上截获而导致泄漏,于是其他人就可以伪装成合法的设备发送数据。所以,我们还需要通信加密技术的支持。
MQTT 协议支持 **SSL/TLS** 加密通信方式。采用SSL/TLS加密之后MQTT将转换为 MQTTS。这有点类似于 HTTP 和 HTTPS 的关系。
我们只要将前面测试的命令修改一下,将 “mqtt://” 改为 “mqtts://”,端口改为 8883就可以用SSL/TLS 加密通信方式连接到 Eclipse 提供的开放 Broker。但是我最近发现它的SSL证书已经过期了因此连接会失败。你在学习这一讲的时候可以再试试万一又更新了呢
所以,我再提供另一个方式,供你测试使用。
我们输入“mqtts://test.mosquitto.org:8883”把开放 Broker 切换到[这个链接](https://test.mosquitto.org/),从链接中下载一个[客户端证书](https://test.mosquitto.org/ssl/mosquitto.org.crt),然后通过下面的命令订阅主题消息:
```
hbmqtt_sub --url mqtts://test.mosquitto.org:8883 -t /geektime/iot --ca-file ~/Downloads/mosquitto.org.crt
```
接着,我们再通过下面的命令测试发布消息:
```
hbmqtt_pub --url mqtts://test.mosquitto.org:8883 -t /geektime/iot -m Hello,World! --ca-file ~/Downloads/mosquitto.org.crt
```
最后在运行hbmqtt_sub命令的终端就可以看到 Hello,World! 的消息:
<img src="https://static001.geekbang.org/resource/image/b0/30/b04f873277f9dba5bfb7e9ff85d17630.png" alt="">
## 小结
总结一下在这一讲中我带你体验了使用MQTT协议的通信过程同时也为你介绍了 MQTT 协议的几个特点。
1. MQTT协议的生态很好。比如MQTT 协议的代码实现非常丰富C 语言的有 MosquittoPython 语言有 Eclipse hbmqtt而且有商业公司在运营相关软件解决方案。这表明 MQTT 协议很成熟。
1. MQTT协议采用了适合物联网应用场景的发布-订阅模式。当然我也提到了MQTT 5.0 中同样增加了请求-响应模式,便于部分场景中的开发使用。
1. MQTT 协议采用二进制的消息内容编码格式,协议很精简,协议交互也简单,这些特点保证了网络传输流量很小。所以客户端(包括发布者和订阅者角色)的代码实现可以短小精悍,比如 C 语言的实现大概只占30KB 的存储空间Java 语言也只需要 100KB 左右大小的代码体积。
1. MQTT 协议在设计上考虑了很多物联网设备的低功耗需求比如Keepalive机制中精简的PINGREQ 和 PINGRESP 这两种消息类型,还有 MQTT 5.0 中新增加的重复主题特性。这也再次印证了MQTT的定位非常明确那就是专注于物联网场景。
1. MQTT 在消息的可靠传输和安全性上,也有完整的支持,可以说“简约而不简单”。
因此在你为物联网系统选择网络协议时MQTT可以作为重点考察对象。在实战篇的动手实验中我们将使用MQTT协议传输设备数据相信你已经做好了准备。
## 思考题
最后,我想给你留一个实践作业和一道思考题。
在介绍 QoS 等级的时候,我没有结合网络抓包来说明消息的交互过程。所以,我想请你在课后使用 Wireshark 工具,实际分析一下各个 QoS 级别的数据包类型。
同时也请你思考一下如果Client 发布消息时选择的 QoS 等级是1 而订阅者在订阅这个主题消息时选择的QoS等级是2 ,这种情况下 Broker会怎么处理呢
欢迎你在留言区写一写你的实践和思考的结果,也欢迎你将这一讲分享给你的朋友一起交流学习。

View File

@@ -0,0 +1,145 @@
<audio id="audio" title="09 | 边缘中心:物联网网关有多重要?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/f7/3f/f79f2e5b38145d5045e6a4231a2f393f.mp3"></audio>
你好,我是郭朝斌。
在进阶篇的前几讲我剖析了物联网中跟设备有关的几个技术点包括物模型、设备的零配置组网、设备进行网络通信要用到的MQTT协议等。
但是,并不是所有的设备都能**直接**接入互联网,直接跟云平台通信。比如智能家居中的一些传感器,它们使用的通信技术是 BLE 或者 ZigBee本身连 IP 地址都没有。那么,这样的设备要怎么联网呢?
## 物联网网关:设备和云平台之间的桥梁
这个时候,我们就需要借助**物联网网关**的能力了。举个例子,我主持设计过一个冷库温湿度监测系统,它是基于 LoRa 通信技术的。
我们知道,冷库的环境是非常复杂的。首先,库房内部的**蜂窝网络信号一般都很差**,一方面是因为要增强保温性能,所以墙壁做得比普通的墙体要厚;另一方面是因为库房位置通常位于城市的郊区,比较偏远,所以经常是完全没有网络信号。其次,库房的**信号环境是千变万化的**,因为会出现满仓、空仓等多种情况。
所以,在冷库内部部署的监测设备,如果直接连接蜂窝网络的话,完全不能实现可靠和稳定的通信。
这种情况下,我选择的解决方案就是,在有稳定网络信号的地方部署物联网网关,让冷库里面的监测设备,通过 LoRa 这种穿透能力强、空旷环境通信范围可以达到几公里的技术跟网关通信,从而**间接**地实现设备的联网。
<img src="https://static001.geekbang.org/resource/image/d6/2b/d6c122e2b43b2c8c9ee860a2945d222b.jpg" alt="">
我们可以看到,物联网网关正在成为整个物联网体系中不可或缺的角色。它作为物联网设备与云平台之间的桥梁,变得越来越重要。
## 协议转换:搭建桥梁的关键
那么,物联网网关凭什么能搭建这座桥梁呢?奥秘就在于**协议转换**。其实,刚才智能家居和冷库的例子,就已经体现了这个功能。
BLE 、ZigBee 和 LoRa 设备在跟网关通信的时候,需要网关基于开放的或者内部私有的协议,解析出数据;然后网关再使用跟云平台的连接协议来组织数据,完成数据传输。
这个过程自然就要求网关设备能够支持不同的通信技术。我在下面给出了一张网关通信接口的结构示意图。
<img src="https://static001.geekbang.org/resource/image/46/1c/46ec62221a9667baab1bb03e42ed691c.jpg" alt="">
看这张图跟看地图一样,上北下南,上半部分叫“北向接口”,下半部分叫“南向接口”。这可不是我在生造概念,而是行业内约定俗成的说法。
**北向接口**需要接入互联网,所以通常的选择有 RJ45 以太网口、光纤接口、Wi-Fi 和 4G、NB-IoT等蜂窝网络模组等。
**南向接口**用来连接物联网设备,除了刚说的 BLE、ZigBee、LoRa、Wi-Fi这些无线技术的接口常见的还有用在**工控机**Industrial Personal Computer工业控制计算机上的 RJ45以太网口、RS232、RS485等有线接口。
这里需要注意的是,每个网关设备的接口类型和个数不是固定的,因为网关产品一般会根据应用场景确定几个不同的规格型号。不同型号的网关需要支持不同类型的协议,以及不同个数协议的转换,所以网关的协议转换功能一般采用插件的软件架构方式。
**插件机制**这种二次开发能力非常重要。一方面,它让我们可以根据接口的情况,动态、灵活地配置协议转换功能;另一方面,它也可以方便我们开发私有协议的解析功能。
比如通过 BLE、ZigBee 或 LoRa 技术跟网关通信的设备,它们通常采用的是私有的应用层协议,这就需要我们基于设备架构设计时定义的私有协议,专门编写解析代码。
至于使用 RJ45网口或 Wi-Fi 跟网关连接的物联网设备,除了采用基于 TCP或者UDP的私有协议之外也可能采用我们之前讲过的 MQTT 或者 CoAP这样标准的协议。这时我们就需要按照这些协议的格式来处理。
另外,工控机 和 **PLC** Programmable Logic Controller可编程逻辑控制器中经常使用的标准协议有 Modbus、ProfiBus、OPC UA 和 BACnet 等。如果你在相关行业,可能对它们有一定了解。这些协议也是需要进行转换的,因为它们一般只应用于工业领域。这里我就不展开介绍了,如果感兴趣的话,你可以在 Wikipedia 上查询到详细的资料。
## 网关的其他功能
经过协议转换,网关就得到了通用格式的数据。对于这些数据,网关还需要进行持久化,把数据临时存储起来。
网关的**存储功能**可以防止因网络临时故障等原因,导致设备数据的丢失。另外,网关和设备的配置信息也需要存储在网关中,以便设备运行过程中快速读取。
同时,数据的**安全性**也非常重要,物联网网关需要做好这几个方面的事情:
1. 完善的**本地身份认证**。这样可以防止网关设备被随意修改软件或者数据。
1. 网关保证**数据的加密传输**。因为很多物联网设备的计算能力非常弱,不具备进行数据加密的能力,这时就需要借助网关来保证数据或者控制命令的加密和解密。
1. 网关能够支持**运营商专网接入**,或者支持**VPN**Virtual Private Network虚拟专用网络技术。这里我补充说明一下VPN技术的好处是基于互联网网络建立加密通道。这样既保证了数据传输的安全可靠又比建立专线成本要低。常用的VPN协议有 IPsec、OpenVPN等。
除了我刚才讲到的协议转换、存储功能和安全管理,物联网网关一般还有**设备管理**、**网关配置**、**空中升级**这些功能模块。它们比较容易理解,我就不在这一讲展开介绍了。
到这里呢,我就把**传统**物联网网关的功能都讲解了。看到“传统”两个字,你可能疑惑了:还有现代网关吗?是的,现在有一个趋势就是越来越强调物联网网关的**数据分析处理**能力。
我在第4讲介绍过数据的分析处理。我们知道云计算平台凭借着强大的处理能力、存储能力和极高的性价比已经成为物联网系统的有力支撑。现在的物联网应用系统主要就是基于云平台实现数据分析处理任务的。
## 在网关上做数据分析
既然如此,那为什么我们还要在网关上运行这些计算的任务呢?这是因为随着物联网的发展,海量的设备接入网络,随之而来的,是更加海量的数据源源不断的产生,并上传到云平台。
这就给云平台提出了很大的挑战。一方面是极大地消耗有限的网络带宽资源;另一方面,网络的不确定因素很多,有可能导致不可控的延时,从而对业务应用造成不可接受的影响。
而且物联网数据通常**与物理实体关系密切**。比如家庭监控摄像头中,家庭成员的肖像等视频信息是非常敏感的;而在工业场景中,很多数据是机密的。如果这些信息全部上传到云平台,会给用户带来很大的安全风险。
所以,现在行业内已经开始尝试将云平台上的部分计算服务,下沉到靠近数据发生地的“边缘设备”上进行,这就是**边缘计算**的由来,而物联网网关是边缘计算中**最轻量级**的解决方案的关键。
## 边缘计算的好处
怎么理解这种技术架构上的进化呢?你可以想一想,人体是如何处理各种感知信息的,是不是所有的信息都需要大脑来处理呢?
其实人体神经系统的信号是**分层级**传递和处理的。我们中学上生物课时都做过类似的试验,如果手指放到蜡烛上,手是下意识地快速移开,然后大脑才意识到发生了什么。这是四肢的一种非条件反射,它是由大脑皮层之下的低级别神经系统,如脑干、脊髓,直接控制完成的。它们距离四肢更近,可以保证在危险的情况下,更加快速地做出反应。
<img src="https://static001.geekbang.org/resource/image/40/45/4051144174eb30fa69a92f3b205f3f45.jpg" alt="">
边缘计算的设计,与人体的这个构造类似。边缘设备,比如物联网网关,完成**初步**的数据处理,和**需要及时响应**的计算任务;而云平台负责需要**大规模数据**和**复杂计算**的数据分析工作,以及完成**整体的协调和控制**。
具体来说,云平台将原有的云计算模型的部分计算任务迁移到网络边缘设备上来;网络边缘设备(比如路由器、移动网络基站等),在数据源附近执行数据处理和数据分析任务。这样一来,就降低了云计算中心的计算负载,减轻了物联网对于网络带宽的压力,提高了数据处理的效率。
我将边缘计算模型的好处,总结为四个方面:
1. **延迟低。**数据只需要从产生设备传输到边缘设备,传输距离短,数据不需要通过其他网络,网络延迟低。
1. **节约了主干网带宽。**缓解大量数据传输所造成的网络拥堵想象。尤其像一些银行的专有网络,本身带宽非常有限,只能用于传输关键性的数据。
1. **计算可用性好。**数据在网络中的路径长度显著变短,因网络波动引起的计算服务不可用情况将有所减少。
1. **隐私性更好。**由于边缘设备距离用户近,用户的隐私数据不再需要上传到云平台,因此,在边缘计算场景下,用户的隐私也可以得到更好的保护。
## 边缘计算对网关的影响
技术架构的变化,一般也会对系统提出新的需求。边缘计算对网关的影响,主要体现在三个方面。
首先,为了更好地在网关中运行边缘计算任务,网关需要支持**虚拟化技术**,这在目前的实践中通常是采用**容器技术**实现。容器天然具有轻量和可移植的优点,非常适合开发人员快速测试应用程序,也方便维护人员在网关上大规模部署和更新应用程序。另外,容器技术也更利于网关使用容器自动运维平台技术,比如 Kubernetes来实现应用程序的编排等功能。
其次,因为网络环境的不确定性,网关需要具备一定的**自治能力**。当网关与云平台的通信中断时,这种情况不应该影响网关处理数据的计算任务,和对物联网设备的管理。
最后,因为边缘计算的引入,我们需要在网关侧实现数据分析处理的任务。而网关的硬件资源非常多样,业务需求也千差万别,所以网关上提供统一的**开发框架**变得更加重要。开发框架可以为开发人员提供一致的API 和组件的互操作能力。这样开发人员可以更加高效地实现业务功能,也更容易和其他厂商协作。
这里我把加入边缘计算能力后的网关架构图放在下面,供你参考。
<img src="https://static001.geekbang.org/resource/image/df/08/dfff704f684838dc0d5d35732d156208.jpg" alt="">
## 怎样实现边缘计算?
我知道,你现在最可能提出的问题就是:有什么开源软件可以助力网关实现边缘计算呢?
我简单介绍一下开源的生态,也方便你更好地理解网关的相关技术。
首先说一下**标准化组织**。目前,在推进边缘计算的标准组织主要有三个,分别是:
1. Linux 基金会下的 [LF Edge](https://www.lfedge.org/about/mission/) Linux Foundation Edge和致力于推进Cloud Native的 [CNCF](https://www.cncf.io/)Cloud Native Computing Foundation
1. Eclipse 基金会下面的 [Eclipse IoT](https://iot.eclipse.org/) 项目
1. OpenStack基金会
至于**容器技术**方面的开源项目,主要有 [KubeEdge](https://kubeedge.io/en/) 项目它的主要思路是将Kubernetes从云端扩展到边缘设备方便应用程序在边缘网关上的编排和调度同时实现云端和边缘设备的协同数据处理。
关于**开发框架**,比较知名的是 [EdgeX Foundry](https://www.edgexfoundry.org/) 项目定位是为工业物联网提供通用的边缘计算框架。为此它适配了很多协议提供出标准的API接口。
专门针对**智能家居领域**的项目有 [Home Edge](https://www.lfedge.org/projects/homeedge/) ,它是三星贡献的一个开源项目,用于加速智能家居设备的边缘部署。
## 小结
总结一下,随着物联网应用场景越来越多,物联网网关正在成为“边缘中心”。它成为物联网的**“云、边、端”架构**(即云平台、边缘、终端三部分)中非常重要的组成部分。你在做物联网网关的架构设计和功能开发中,需要重点关注的内容有:
1. 物联网网关是设备与云平台之间的桥梁,网关的南向接口和北向接口都非常多样。因此协议转换功能是必须包含的功能。它保证了物联网设备数据的可靠上传,和云平台控制命令的有效执行。
1. 关注网关基础功能的实现。这些功能包括存储功能、安全管理、设备管理、网关配置和空中升级这些功能模块。它们既保证了数据的安全和可靠传输,也为远程维护提供了支持。
<li>你需要根据业务场景决定网关对于边缘计算的支持能力。边缘计算对于物联网网关的架构有3个方面的影响<br>
1 容器技术。容器技术的应用利于开发人员快速开发、测试应用程序,也方便维护人员在网关上大规模部署、更新应用程序。同时,基于容器自动运维平台技术可以实现应用程序的编排等功能。<br>
2自治能力。它可以保证网络环境不稳定时网关的数据分析处理任务的正常执行。<br>
3开发框架。开发框架利于开发人员的开发工作也有益于各部分组件的协同互操作。</li>
## 思考题
最后,我还是给你留一个思考题。
边缘计算已经成为物联网的必要组成部分,行业内云计算企业、网关设备商、运营商都积极参与推进技术体系的发展。那你有使用这些边缘计算产品吗?应用在什么业务场景呢?
欢迎你在留言区和我交流,也欢迎你将这一讲分享给你的朋友一起讨论学习。

View File

@@ -0,0 +1,174 @@
<audio id="audio" title="10 | 数据处理框架:批处理还是流处理?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a3/cc/a3be9d589fcefb7609eefe6e4955afcc.mp3"></audio>
你好,我是郭朝斌。
在[第4讲](https://time.geekbang.org/column/article/308366)中,我分析了物联网系统的数据技术体系。它包括 5 个部分:数据源数据采集、数据传输、数据存储、数据处理和数据应用。
不过这还只是一个整体的认识框架。数据技术体系涉及的内容很多虽然我在第4讲已经介绍了**数据应用**中用到的分析方法和算法,但是你还需要在这个框架的基础上,继续了解其他几个部分的知识。
所以我会从今天开始用连续3讲的篇幅分别讲一讲数据处理、数据存储和数据传输涉及的技术。每一讲分别专注其中一个主题把它们都剖析透。至于数据源的数据采集它跟具体的行业应用有关不同的行业差别很大所以我们这门课就不展开讲了。
<img src="https://static001.geekbang.org/resource/image/6c/38/6c871e4476c3aa93f37c9c3e030e1c38.jpg" alt="">
## 处理海量数据时的难题
我们知道数据分析需要用到很多算法比如支持向量机和K-means。那么在物联网系统的应用中我们要怎么使用这些算法呢
你可能会想:这算什么问题?从文件中或者数据库中读取数据,然后使用一个算法工具,比如 Python 语言的机器学习框架 **Sklearn**(也称为 Scikit-Learn不就可以快速应用算法处理数据了吗
其实没有这么简单因为这种方式一般只适合用来学习和做研究。在真实的物联网场景中你面临的是海量的数据。当我们面对海量数据的处理时一切就不是这么直接和简单了。先不说高效地处理首先你面临的挑战就是如何把高达几GB甚至数TB的数据直接读取到内存中计算显然直接加载到内存是不现实。
所以,对于海量数据,我们要借助大数据处理技术。
## 经典思路MapReduce的分而治之
那么,大数据处理技术是采用什么思路解决海量数据的处理任务的呢?
为了让你更好地理解,在解答这个问题之前,我想先跟你讲一个故事。
记得上大学的时候,为了提前体验一下工作招聘的流程,我参加过一些公司的笔试。笔试题目中有很多是关于**大文件处理**的,比如给你一个或者几个很大的文件,问你怎么找出其中出现频次 Top 100 的词。搜索引擎公司尤其爱出这种题。
对于这种问题,如果我们要提高处理的效率,就需要考虑**“分而治之”**的策略了把数据分成几份分配给几台计算机同时处理每台计算机统计它负责的文件块中每个词出现频次然后再将所有计算机统计的结果进行汇总最终得到所有数据中最高频的100个词。
虽然这只是一道笔试题,但是它来源于搜索引擎公司真实的业务需求。搜索引擎需要对海量的网页内容进行处理,建立索引,计算权重。为此,工程师们要做很多事:
- 数据切分:把大文件分成小文件
- 数据传输:把文件分发给可用的计算机
- 结果汇总:把每台计算机的计算结果做汇总处理,得到最终结果
- 容错处理:解决多台计算机协作过程中出现的机器故障
- 灵活扩展:根据计算机临时的增加,随时调整计算任务的分配和汇总
显然,这不是一项轻松的工作,还涉及到很多分布式系统的技术。如果每次有不同需求的时候,我们都得重新走一遍这个过程,就要投入大量的时间和精力,太不划算了。
怎么解决这个问题呢?方法当然有很多。在大数据技术的早期,应用最广泛的方法是 MapReduce ,流行的原因很简单,就是分享和开源。
首先,**谷歌**Google基于公司内部的实践在2004年发表了**分布式计算框架**的[论文](https://users.soe.ucsc.edu/~sbrandt/221/Papers/DataManagement/dean-osdi04.pdf)。这篇论文提出了 **MapReduce** 计算框架的设计思想,主要用于解决海量网页的索引生成问题。
接着,开源搜索引擎项目 **Nutch** 的开发人员,基于这个设计思想开发出了开源的 [**Hadoop MapReduce**](https://hadoop.apache.org/docs/r1.2.1/mapred_tutorial.html) 实现。
MapReduce 是怎么设计的呢?其实他们的想法跟刚才那道笔试题的解法一样,也是分而治之。
具体来说,就是把数据分成相同大小的多份,然后相应地创建多个任务,并行地处理这些数据分片,这个的过程被定义为**Map**过程接着再将Map过程中生成的计算结果进行最终的汇总生成输出结果这个过程被定义为**Reduce**过程。
这两个过程合起来就是MapReduce了。
这个设计思路本身,还不是最关键的地方。更重要的是,它提供了一个**框架**,把与计算机硬件相关的容错和扩展功能都实现了。同时,它也提供了统一的开发接口,我们只需要基于业务目标,定制 Map 和 Reduce 的具体计算任务就行了。这就大大降低了我们分析海量数据的难度。
当出现一个好用的工具时,人们就会试图用它来解决一切问题。随着 MapReduce的流行人们开始把它应用在各种场景中而不仅仅是计算索引比如执行 Hive 中的 HQL 查询(这是一种 SQL样式的交互式计算
这个时候MapReduce就显得越来越“力不从心”了原因主要有两个方面。
一方面MapReduce的计算模型非常简单只有Map和Reduce两种类型。就连对数据进行排序和分组这样简单且常见的任务时都需要转换成Map和Reduce来进行而像上面说到的 HQL 查询,更是需要使用多个 Map 和 Reduce过程才能实现。
这有点像函数调用。我们使用C、Java和Python这些高级语言的时候直接引用函数名填上函数参数就可以了。但是如果我们使用的是汇编语言就需要自己写代码实现函数入参的压栈、返回地址压栈、跳转到函数代码的地址、执行完成后的出栈和返回等操作。
这非常不直观,也容易出错。
另一方面MapReduce 是基于分布式文件系统 HDFS 来实现数据存取的。注意不只是读取源数据和写入计算结果包括中间的计算结果的存储和数据交换也是基于HDFS的。
HDFS 是磁盘上的文件系统读写的效率要远远低于内存。HDFS之所以选择磁盘作为存储介质是因为它出现的时代计算机内存还是很昂贵的。
这就导致 MapReduce的效率不高。
## 高效率开源框架以“快如闪电”为目标的Spark
高效是工程师们一直追求的,不管是开发还是处理,我们都希望越快越好。为了实现高效,新的设计思想和数据处理框架开始出现,其中的翘楚是 Spark 项目。
那么Spark是如何打造高效率框架的呢
首先在计算模型上Spark 抛弃了MapReduce的两个过程模型采用了**DAG**Directed Acyclic Graph有向无环图模型。为什么采用DAG呢我给你挖掘一下这背后的本质。
下面这张图展示了 MapReduce 处理数据时的数据流:
<img src="https://static001.geekbang.org/resource/image/8f/da/8f34418d89dfa6318024444596206cda.jpg" alt="">
学过数据结构和算法的你,一定知道这就是**有向无环图**。所以采用DAG来描述数据处理的过程应该说是反映了数据处理过程的本质。这样一方面开发人员可以更容易地描述复杂的计算逻辑另一方面计算框架也能更方便地自动优化整个数据流比如避免重复计算。
其次Spark 的数据存取充分地利用了**内存**。
它的数据分片被称为**Partition**。然后它基于Partition提出了**RDD**Resilient Distributed Datasets弹性分布式数据集的概念。
所谓的**“弹性”**就是指,数据既可以存储在磁盘中,也可以存储在内存中,而且可以根据内存的使用情况动态调整存储位置。这就提高了计算的效率。
## 另一种思路:为实时计算而生的流处理
到这里,你可以想要问:怎么还没有说到批处理和流处理呢?
其实我刚才介绍的MapReduce就是批处理的经典思路和框架而Spark就是目前更高效、更流行的数据批处理开源框架。
之所以没有在一开始的时候就提出来,是因为“批处理”这个概念一定是相对于其他处理方式来说的,比如流处理。如果后来没有流处理模式,我们也只会说“大数据处理”或者“分布式数据处理”,而不会专门定义一个批处理出来。
那流处理为什么会出现呢?当然是因为业务需求。随着社交网络的出现,产品中的个人**信息流**Feeds需要基于好友关系和好友的发布动态快速地计算和显示出和本人有关系的信息。类似的需求还有个性化的广告和消息推送服务。
而在物联网中,当采集的数据传输到系统后,我们可能需要对数据进行一些预处理,处理之后再存储起来。
这些需求在现在的应用中很常见。它们的共同特点是,数据像流水一样流入系统,然后被处理,而数据的快速处理,也就是实时计算,是这个过程中的关键点。这就是流处理出现的背景。
那怎么实现呢?考虑到数据输入的速度和数据处理的速度不一定一致,我们可以按照一定的分配策略,将数据输入多个消息队列中缓存数据,每个消息队列由一个进程或者线程处理数据。
但是和我一开始提到的计算词语出现频次的例子一样,这种基于消息队列自己开发的系统,同样会遇到拓展性、容错性的问题;另外,还要保证消息队列中消息的可靠传输。
所以一些流处理框架开始出现,一方面解决这些问题,另一方面也给开发人员提供统一的开发接口,从而方便流处理的任务的开发和实现。
## 流处理开源框架Storm、Spark Streaming 和 Flink
这其中最早的代表就是社交网络公司Twitter开发的**Storm**框架。
Storm的一个重要概念就是**数据流**Stream。相对于批处理针对数据块的处理方式所谓的流处理就是针对数据流的处理方式。Storm把Stream描述成是**元组**Tuple构成的一个无限的序列如下图所示
<img src="https://static001.geekbang.org/resource/image/84/40/840228bf5b3ffc00af6a890f10e31040.jpg" alt="">
Stream 从**水龙头**Spout中产生也就是说Spout把需要处理的数据转换为由Tuple构成的Stream。然后Stream经过**转接头**Bolt的处理输出新的Stream。其中Bolt的处理可以是过滤、函数操作、Join等任何操作。你可以参见下面的流程图示例
<img src="https://static001.geekbang.org/resource/image/4c/9a/4c251b0abc56d4f8438fd832ab94229a.jpg" alt="">
图片中的Spout、Bolt 和Stream共同构成了Storm中另一个重要概念**拓扑**Topology
你应该可以看出来Topology是一个DAG有向无环图。Storm框架中运行的正是一个个Topology而且因为是流处理它会一直运行直到被手动终止。
基本上和Storm同时出现的流处理开源框架是Spark Streaming。看到Spark Streaming你可能疑惑Spark的计算引擎不是基于RDD数据集也就是**数据块**来处理数据的吗?它要怎么处理**数据流**呢?
其实无论是数据块还是数据流,都只是数据的不同使用和处理方式,它们之间是可以相互转换的。
这就像在一些编程语言标准库中的**File**操作接口File本身在磁盘中是按照块存储的但是File操作的接口可以按照流Stream的方式读写文件。同样地用户键盘输入的Stream或者通过网络连接Socket接收的数据流也可以先缓存起来然后作为整块的数据统一处理。
Spark Streaming 正是将数据流转换成一小段一小段的**RDD**。这些小段的RDD构成一个流式的RDD 序列,称为**DStream**,所以它的流处理被称为**“微批处理”**。
<img src="https://static001.geekbang.org/resource/image/71/95/71yyf920db9b1619ee4ee700108fd995.jpg" alt="">
显然它的实时性取决于每小段RDD的大小实时性不如Storm框架不过这种方式也使它的吞吐能力要大于 Storm。
整体来看你可以认为Spark包括Spark Streaming基于数据块的数据模型同时提供了批处理和流处理的能力。
那么既然数据块和数据流可以相互转换,是否存在基于数据流的数据模型,然后同时支持批处理和流处理的开源框架呢?毕竟数据输入系统的本来方式就是数据流,这样理论上可以获得更好的实时性。
答案是有的,比如**Flink**。Flink将数据块作为一种特殊的数据流通过从文件等持久存储系统中按照Stream的方式读入和处理来提供批处理的能力。在这个基础之上Flink提供了统一的批处理和流处理框架也就是所谓的**“流批一体”**的数据处理框架。
Flink虽然出现的时间不长但凭借着优秀的设计性能非常强延迟可以低到微秒级别是对实时计算性能要求的高的场景的理想选择。行业内阿里云和腾讯云对于 Flink 的支持都非常好很多企业也在实践中逐渐尝试使用Flink来替代Storm框架。
## 小结
总结一下,在这一讲中,我介绍了物联网系统的两类数据处理框架,顺便讲了很多大数据处理技术的起源和设计思想。这不是我想啰嗦,而是因为学习一个东西的时候,最有效的方式就是搞清楚它的底层原理,把握它的发展脉络。只有这样,每个知识点才能各归其位,遇到问题时你就可以顺藤摸瓜地去分析、去解决。
今天的重点,这里我再概括一下:
1. 批处理适合海量静态数据的非实时处理,延迟比较高,也叫离线计算,主要用于离线报表、历史数据汇总等场景。
1. 流处理适合动态输入的流式数据的实时处理,延迟低,也叫实时计算,主要用于实时监控、趋势预测、实时推荐等场景。
1. 批处理可以选择的开源框架有Spark和Flink。至于Hadoop MapReduce你了解一下基本原理就可以了它在应用中应该已经被放弃了。当然如果你有遗留系统仍然使用MapReduce那就只能维护着或者找机会迁移到新的框架。
1. 流处理的开源框架可以选择 Storm、 Spark Streaming和Flink等。
另外,我还做了一张思维导图,供你在使用中参考。
<img src="https://static001.geekbang.org/resource/image/74/5c/74c223220482f0151a023fee44a8125c.jpg" alt="">
技术的发展是需求推动的。随着互联网上网页数量的增多,从搜索引擎开始,大数据处理相关的技术经历了萌芽到成熟的快速发展过程,已经在电商推荐系统、广告营销、金融科技等领域得到广泛的应用。
未来随着物联网的发展,智能家居、智慧城市、工业物联网的领域应用越来越多,数据量更是极速膨胀。这一定会对大数据技术提出新的挑战和需求,新的计算框架也许也会出现,因此这是一个非常活跃的技术分支。不过,你在了解、学习新的框架时,都可以回到我这里讲的数据处理的本质来思考。
## 思考题
最后,给你留一个思考题吧。
这一讲我们讨论了很多批处理和流处理的内容,我们知道一个完整的业务系统,一般既需要批处理,也需要流处理,那这些不同的数据处理框架在系统中应该如何配合呢?或者说数据处理系统的架构应该是怎样的呢?
欢迎你在留言区谈一下自己的看法,或者分享一下你工作中应用的架构方式。如果你有朋友对物联网感兴趣,也欢迎你将本课程分享给他们,一起交流学习。

View File

@@ -0,0 +1,170 @@
<audio id="audio" title="11 | 数据存储:物联网中的数据库有哪些?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/79/22/7920ba1a722aafea70d678a3b0d13722.mp3"></audio>
你好,我是郭朝斌。
上一讲,我讲解了物联网系统进行数据处理的思路和常用的开源框架。那么,这些数据要从哪里读取呢?还有,数据处理完成的结果要写入到哪里呢?这就涉及到数据存储方案。
跟灵活多变的数据处理框架比起来,数据存储方案要固定得多。
为什么这么说呢?因为确定了数据存储方案,其实也就确定了用来存储数据的具体软件。当系统开发出来并投入使用后,数据就会源源不断地流入软件存储起来。
当你因为存储软件的读写性能无法满足需求,希望重新做软件选型的时候,就会发现把数据从一种软件迁移到另一种软件是一件费时费力还容易出错的事情。它就像给飞行中的飞机换引擎,难度可想而知。
更何况数据是一种重要的**数字资产**,之前的数据公司是不能轻易丢弃的。
所以一旦确定存储软件,一般很难切换。这就要求我们做软件选型的时候,必须非常谨慎。因为一旦做出错误的决策,将来可能追悔莫及。
那么怎么正确地选型呢基本的原则还是我在第4讲中提到的根据数据类型来选择。
数据大体上可以分为3类
1. 结构化数据Structured data
1. 半结构化数据Semi-structured data
1. 非结构化数据Unstructured data
这一讲,我会一类一类地为你介绍选型经验。
## 结构化数据:关系型数据库
我们先来看看**结构化数据**。
结构化数据是指具有**明确的**、**固定的**结构关系的数据。
怎么理解呢你可以回想一下班级通讯录的Excel表格表头有姓名、性别、生日、家庭住址、手机然后下面是一行一行的数据。正因为表头有明确和固定的结构所以每一行的数据的结构都是一样的。这种用二维的表结构就可以表示的数据就是结构化数据。
<img src="https://static001.geekbang.org/resource/image/16/69/1634466byya14757dfc4115yyb5ed769.jpg" alt="">
这个表结构和关系型数据库中的表结构非常一致,所以结构化数据适合使用**关系型数据库**来存储。基于关系型数据库我们可以很容易地实现数据的“增删改查”而且可以方便地实现事务操作保证数据操作满足ACID特性也就是原子性Atomic一致性Consistency隔离性Isolation和持久性Durability
举例来说,共享单车中用户的个人信息、骑行记录和支付信息等这类属性明确,而且对于一致性要求很高的数据,一般就采用关系型数据库来存储和操作。
不过随着业务的发展,当物联网系统面临海量数据时,单机数据库的计算和读写性能会受到影响。这时,我们已经不能简单地通过提高计算机的硬件性能(垂直扩展)来解决了。
### 分布式关系型数据库
为了应对大数据对计算和读写性能的挑战,数据库系统需要借助分布式架构的威力(横向扩展)。
这就像为了解决“灭霸”这个难题,我们当然希望出现一个更加强大的超能力者。但是即使是钢铁侠和绿巨人,也是有能力瓶颈的,所以就需要各种超级英雄组成联盟才能达成目的。
那么怎么用分布式架构来解决大数据的问题呢?行业内一开始的实践是对数据库进行**分库分表**,然后借助**数据库中间件**实现关联查询、主键避免重复、分页查询和事务一致性等功能。
但是这种方式有很多弊端比如分库分表需要根据业务数据的特点仔细设计而且这种分库分表操作一定会涉及非常麻烦的数据迁移工作通过中间件执行SQL的效率不高事务一致性很难保证往往需要在应用中实现“最终的一致性”。
为了解决这些问题,**分布式关系型数据库**出现了。它也经常被称为**NewSQL数据库**。它的优势主要体现以下3个方面。
1. **高扩展性**。NewSQL天生支持数据**分片**,支持动态增加节点,不需要进行麻烦的数据迁移工作,所以能够轻松地满足数据不断增大时的存储需求。
1. **高并发性**。相比于单机关系型数据库基于**磁盘**的设计NewSQL在设计上更好地利用了**内存**所以SQL执行效率很高。在事务的支持上NewSQL有着高效的分布式事务特性。所以它可以实现海量数据的读取和写入以及大量用户的查询和更新等操作。
1. **高可用性**。NewSQL采用Paxos或者Raft协议来实现**多副本**的存储,而且还支持自动选择主节点,保证了数据库的故障切换时间很短。
所以,对于需要**强一致性**事务的场景(比如共享单车物联网系统中的支付交易),以及需要基于关系模型进行**复杂查询**的场景(比如共享单车中涉及用户信息表、单车信息表和骑行记录表的丢失单车查询),当单机关系型数据库已经无法满足大数据需求时,可以考虑分布式关系型数据库。
尤其是当之前的业务已经基于关系型数据库开发完成并且在实际服务环境中运行的时候分布式关系型数据库简直是业务开发人员的福音。因为这种情况下想改用其他类型的数据库如果数据库不支持SQL语言业务代码就需要进行大量的修改和调整。
在实践中分布式数据库的开源选择有TiDB、CockroachDB等也有商业化的产品比如阿里巴巴的OceanBase等。
### 时序数据库
分布式关系型数据库这个方案,是从**增加能力**的角度得到的。那么,我们能不能从**简化问题**的角度出发,找到其他解决方案呢?
从原理上看是可行的。这就像假如把灭霸的手套去掉,也许作为高中生的蜘蛛侠也可以对付他。
物联网中传感器的应用系统就是这样的场景比如监控冷库中温度和湿度条件的传感器。这些传感器会按照一定的周期不断地上报数据比如每1分钟上报1次。这样的数据按照时间顺序排列形成了一系列的数据点所以被称为**时间序列数据**Time Series Data简称**时序数据**。
时序数据在读写、存储和分析处理方面有下面这些特点:
1. 时序数据是持续地写入,一般是采用固定的频率,没有写入量忽大忽小的明显变化。数量非常大,而且并发写入的需求也很高。但是数据很少做更新,旧数据除了特殊情况下的修改,基本是不需要更新的写入操作。
1. 时序数据的读取很少,相比写入的高并发和高频率,读取的需求主要是进行数据分析的应用,而分析应用的并发访问量是比较少的。
1. 时序数据时效性很强,一般是越新的数据价值就越大,旧数据会迅速失去价值。
1. 时序数据的数据分析主要关心的是新数据,旧数据被查询和分析的概率不高。旧数据一般是粗颗粒度的读取分析。而且在被分析时,一般是基于时间范围读取分析,读取某一条记录的几率很小。
基于时序数据结构和应用上的特点,人们开发出了**时序数据库**,它在近些年随着物联网的应用变得非常流行。
时序数据库简化了关系型数据库很多不必要的功能比如采用读取性能不高的LSM 树代替 B+树的存储结构。它专注于支持**高并发的数据写入**,采用更高压缩比的压缩算法来支持海量数据的存储,降低存储的成本,同时,通过预处理等方法来支持海量数据的高效分组聚合计算。
那么,时序数据库的具体产品有哪些呢?
首先是开源软件你可以选择InfluxDBKairosDB和OpenTSDB等产品。
另外云服务企业一般都开发了自己的时序数据库比如阿里巴巴的TSDB和亚马逊的AWS Timestream等。其中TSDB还扩展支持了空间信息以便处理地理围栏和空间轨迹等需求。
## 半结构化数据:非关系型数据库
接下来,我们再看看**半结构化数据**。
半结构化数据包含相关标记用来分隔语义元素以及对记录和字段进行分层。比如JSON格式的数据我们在[第6讲](https://time.geekbang.org/column/article/310441)的物模型中应用过它就是一种半结构化数据。JSON中大括号“{}”,中括号"[]",冒号":",逗号","就是**分隔语义元素**,每个冒号前面的内容是**字段**,后面的是**记录**。
我拿共享单车的运行轨迹数据作为例子展示了一条JSON数据你可以参考。
<img src="https://static001.geekbang.org/resource/image/21/c3/21f1216223d58619df0c561d7e41d9c3.jpg" alt="">
半结构化数据的特点是,它的结构并不固定,属于同一类实体可以有不同的属性,这表明它有很好的可扩展性。另外,即使它们被组合在一起,这些属性的顺序并不重要。这些特点决定了我们很难按照关系型数据库的数据模型来建立半结构数据的结构和相互之间的关联。
除了JSON常见的半结构数据还有XML等。在应用系统中呢日志文件就是典型的半结构化数据。
为了更有效地存储半结构化数据,我们可以选择**NoSQL数据库**。它以键值对存储,且结构不固定,每一个元组可以有不一样的字段。每个元组可以根据需要增加一些自己的键值对,这样数据库就不会局限于固定的结构,可以减少一些时间和空间的开销。
比如在共享单车系统中我们有一款新型单车它除了可以基于蜂窝网基站定位外还可以基于GPS进行精度更高的定位。那新、老单车的运行轨迹数据就会有区别。你可以参考下面的一张对比图。
<img src="https://static001.geekbang.org/resource/image/1a/06/1a62124025840c32ddee7b3d3d435f06.jpg" alt="">
<img src="https://static001.geekbang.org/resource/image/75/95/751fa86f285443c0a3403bfb1a261f95.jpg" alt="">
也就是说你可以根据需求去添加自己需要的字段。比如在获取用户的不同信息时你不需要像关系型数据库那样对多表进行关联查询而是只需要根据id取出相应的value就可以完成查询。
其实我们知道SQL只是一种操作数据库数据的接口所以NoSQL中的“No”真正表达的意思是 No Relational专业的叫法应该是**非关系型数据库**。
非关系型数据库由于不再强调数据的一致性,不支持事务操作,也不再关注复杂的关联表查询,所以它对海量数据的处理性能更好,而且存储的数据格式比较丰富,易于扩展。有些非关系型数据库会使用内存来存储数据,以便支持更快的查询速度。
非关系型数据库也有很多的开源产品比如CouchDB、Redis、HBase、Cassandra等你可以根据熟悉程度和生态支持选择。商业化的选择也有MongoDB和Oracle NoSQL等产品。
## 非结构化数据:分布式文件系统
虽然半结构化数据的结构会根据需求变化,但多少还保留了一些结构化的描述信息。可是有一类数据却完全不能按照结构化的方法来描述,这就是**非结构<strong><strong>化**</strong>数据</strong>。典型的例子有监控系统中的视频、图片和音频等信息。
非结构化数据没有预定义的数据模型,无法简单地用数据库二维表结构来表现。它的格式非常多样,标准也不是统一的。对于这一类数据的大规模存储,我们只能使用**分布式文件系统**。
其中最有名的当然就是 Hadoop 实现的一个分布式文件系统Hadoop Distributed File System简称HDFS。因为拥有Hadoop的大数据生态所以它是分布式文件系统的最理想的选择之一。其他的分布式文件系统还有FastDFS和Ceph等。
## 小结
总结一下,在这一讲中我主要讲解了关于数据存储方案的选择。重要的事情说三遍:
选型一定要慎重!
选型一定要慎重!
选型一定要慎重!
错误的决策不仅会影响系统的性能,还会增加成本投入。
基于不同数据类型的特点,选择方案的原则如下:
1. **结构化数据**,比如用户和设备的关系,用户信息、设备参数等,还是适合**关系型数据库**。为了应对海量数据,你可以采用**分布式数据库**有TiDB、CockroachDB等也有商业化的产品比如阿里巴巴的OceanBase等。另外物联网中的传感器设备随时间不断产生新数据。要存储这类数据你可以选择**时序数据库**,来获得更高的读写和查询性能。
1. **半结构化数据**比如JSON结构的数据日志记录等一般采用 **NoSQL 数据库**产品。常见的开源选择有MongoDB、CouchDB、Redis、HBase和Cassandra等商业化的选择有MongoDB和Oracle NoSQL等产品。
1. **非结构化数据**比如视频和音频一般采用分布式文件系统。Hadoop 体系中应用广泛的 **HDFS** 是非常理想的产品。除此之外FastDFS和Ceph也可以作为选择。
我整理了一个数据存储方案相关的思维导图,供你参考。
<img src="https://static001.geekbang.org/resource/image/da/82/da7262dd01754e0cac5e2e76a5b8fb82.jpg" alt="">
## 延伸:数据迁移工具
在这一讲一开始的时候我就说过数据的迁移工作比较麻烦需要在设计阶段就尽量避免。但是有些数据迁移的需求可能是你避免不了的。比如你要把已运行系统中MySQL数据库里的数据导入到NoSQL数据库HBase中以便使用大数据技术进行分析处理。
对于这样的需求,我们就得借助工具来完成,比较有名的工具就是**Sqoop**项目。它在 Hadoop 大数据存储系统和关系型数据库等系统之间架起了桥梁借助Sqoop你就可以很方便地把MySQL数据导入到HBase中。
另外Sqoop采用了被称为**Connector**的插件架构不同的Connector还可以对接不同的数据源而且你也可以根据自己的需求定制专属的Connector 来完成一些特殊的迁移工作。所以Sqoop也可以完成NoSQL数据库比如CouchDB和文件存储系统比如FTP之间的迁移任务。
<img src="https://static001.geekbang.org/resource/image/8f/13/8ffd33446f04d3d1c0f497964c2aa513.jpg" alt="">
当然Sqoop也有一个缺点那就是不太适合**增量的数据更新**,又叫**CDC**Change Data Capture。如果基于Sqoop采用定时扫描整张表的方法那么执行会比较低效延时也比较严重。
这时你可以考虑LinkedIn开源的Databus项目或者阿里巴巴的Canal它们都是基于分析数据库日志文件来高效地实现数据的增量更新。
## 思考题
最后,给你留个思考题。
除了从数据类型上考虑在选择数据库时我们还要结合业务的需求来判断因为我们的最终目的是高效地、低成本地解决业务问题比如时序数据库可以更高效、更低成本地解决传感器监控应用的数据存储需求。你能从处理性能和成本的角度分析一下NewSQL数据库、NoSQL数据库和分布式文件系统针对的业务分别有什么特点吗
欢迎在留言区谈谈你的思考,也欢迎你将这节课分享给你的朋友一起学习。

View File

@@ -0,0 +1,132 @@
<audio id="audio" title="12 | IoT Hub面对海量设备如何打造高性能设备接入层" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/0f/fe/0f05f8beb53fe09b2fbd3144996995fe.mp3"></audio>
你好,我是郭朝斌。
前面两讲,我们一直在谈物联网云平台针对海量数据的处理和存储技术。顺着物联网的数据技术体系继续往下探索,我们自然就会面临一个问题:物联网云平台的服务器需要管理海量设备的接入,并且接收来自设备的海量数据的输入,那么服务器要怎么应对这样的挑战呢?
我们来分析一下这个问题。物联网设备是通过某种通信协议接入云平台的比如常用的MQTT协议那么设备接入的服务器就是MQTT Broker服务器。
从架构设计来说,负责设备接入的这一部分一般叫作**设备接入层**,也被称为**IoT Hub**。设备接入层之后,才是和互联网系统类似的**业务层**。具体的系统架构图可能是下面这样的:
<img src="https://static001.geekbang.org/resource/image/fd/5b/fdb74fcd872c299459bbb2f28383325b.jpg" alt="">
不过估计你也发现问题了这样的架构根本无法支撑物联网场景中海量设备的接入和海量数据的输入。单台MQTT Broker服务器很容易面临性能瓶颈。所以前面提到的“服务器怎么应对挑战”的问题就变成了怎么打造高性能的设备接入层
我们再继续深入分析,把关键点定位得更具体一点。打造**高性能**的设备接入层,最重要的技术难点是,如何实现接入层的**高并发**。因为只有具备高并发的能力,才能有效地、可靠地实现数据的传输。
所以,现在问题又变成了:物联网云平台中,怎么实现高并发的设备接入层?
接下来,我就带你一步一步地设计一个高并发的设备接入层。在这个过程中,我会为你详细地讲解我们需要用到的重要技术,让你为物联网平台的搭建做好技术储备。
## 负载均衡:让多台服务器更好地协作
[上一讲](https://time.geekbang.org/column/article/316274)曾经我提到为了解决灭霸这个难题要让超级英雄们组成联盟。同样地面对性能瓶颈我们可以集合多个服务器的力量来解决问题也就是说可以通过增加多台MQTT Broker服务器来满足海量设备的连接请求。
但是服务器数量变多之后它们具体要怎么协作呢比如一个MQTT请求过来了它应该被分配给哪台服务器处理呢更进一步地说怎么保证每台服务器的负担和压力都基本平衡呢
这就要用到负载均衡技术。**负载均衡**Load Balancer这个名字非常形象我们可以分成“负载”和“均衡”两个词来理解。
- **负载**是指服务器面对的网络连接和服务请求的**压力**,也就是 “困难”。
- **均衡**是针对服务器压力的解决办法,多个服务器一起来处理任务,并且这几个服务器的压力要达到**平衡**的状态,也就是“分担”。
<img src="https://static001.geekbang.org/resource/image/1e/32/1e6b6eeff26f3ed5c15debfc3df2fe32.jpg" alt="">
这种平衡状态要怎么实现呢?我们需要设计一种任务**分配策略**。
最简单、直接的办法就是**平均分配**。
比如有3个服务器都在等待分配。当一个服务请求到来时负载均衡服务器先将它给服务器1处理接着第二个请求到来时给服务器2处理以此类推。
当第四个服务请求需要处理时负载均衡转回来再次指定给服务器1循环轮转。所以这种策略也叫**轮询策略**Round Robin Scheduling
如果用公式来表达这个服务器序号的计算方法,那就是:
$$i = (i+1)mod(n)$$
其中 i 是服务器的序号从0开始计数n表示服务器个数这里n等于3。
当然,在现实环境中,各个服务器的配置并不是完全一样的,有的任务处理能力强,有的能力弱些。这就像在复仇者联盟中,每位英雄的强度也各有不同。
我们自然希望能力强的服务器多负担一些任务,所以对于轮询策略,我们可以引入**权重系数**。
比如服务器1的权重是2服务器2的是4服务器3的是1那么负载均衡服务器就可以给服务器2多分配任务而给服务器3分配最少的工作。
这个改良版的策略就是**加权轮询策略**Weighted Round Robin Scheduling
不管是轮询策略,还是加权轮询策略,它们具有简单实用的优点。但是通常情况下,服务器处理每个任务的时间是不同。尤其是在物联网场景中,设备可能会跟服务器保持长连接。可能有的服务器被分配的设备比较少,但是保持连接的设备却很多。这种情况下,如果我们仍然使用轮询策略,那么各服务器的负载就很难达到平衡状态。
所以我们需要一种**动态调度**的策略,能够基于各服务器的网络连接数情况,优先将新的任务分配给保持连接的设备数最少的服务器。这种基于连接数的策略就是**最小连接数策略**Least Connection Scheduling
我们也可以为最小连接数策略引入权重系数,给不同能力的服务器分配不同的权重,更好地平衡服务器的负载,这就是**加权最小连接数策略**Weighted Least Connection Scheduling
这个策略是怎么计算的呢?假设服务器的权重分别是$W_1$、$W_2$和$W_3$,服务器的连接数分别是$C_1$、$C_2$和$C_3$,那么计算的表达式是这样的:
$$Min(C_1/W_1, C_2/W_2, C_3/W_3)$$
负载均衡服务器会选择计算结果是最小的那台服务器。
如果有2台或者3台服务器的计算结果同时最小呢这时我们还可以在这几台服务器中采用轮询策略。所以说分配策略不是相互排斥的在实践中我们需要综合使用不同的策略。
考虑到这个原因,我需要给你再讲一种常用的分配策略,那就是**源地址哈希值策略**Source Hashing Scheduling
所谓源地址,一般就是指网络连接的**源IP地址**。负载均衡服务器通过计算源IP地址的哈希值来确定对应的服务器。因为相同IP地址的哈希值是不变的这就保证了相同的设备可以连接到固定的服务器上。
在实践中,我们可以通过开源软件来搭建负载均衡服务器。比如 **HAProxy 软件**,它支持[OSI网络七层模型](https://en.wikipedia.org/wiki/OSI_model)中第4层和第7层的负载均衡性能可以跟商用解决方案媲美。同时我建议搭配**Keepalived**软件使用,实现高可用的热备方案,这样就可以避免单机故障导致系统瘫痪了。
## 消息队列:避免耗时的等待
当负载均衡服务器将物联网设备的网络请求分配到 MQTT Broker 服务器后MQTT Broker服务器就可以与设备建立连接并且收到设备上传的数据了。然后数据就会传输给数据流处理服务器或者写入数据库中。
但是数据流处理和把数据写入数据库都是花时间的操作它们和数据传入的速度并不一致。为了可以让MQTT Broker服务器高效地完成数据的传输同时保证数据流处理和写入数据库的操作可靠执行我们需要在MQTT Broker服务器和数据流处理服务器之间加入**异步处理**机制。
那么,异步处理机制要怎么实现呢?行业内一般通过**消息队列**来实现。你可以把消息队列想象成双十一购物节后的快递公司,它收到商家大量的运单,然后快递公司按照一定的节奏完成这些运单的递送任务。
在我们的系统中MQTT Broker 服务器将数据给到消息队列,就完成了数据传输的工作。在这之后,数据流处理和写入数据库的操作只要按照自己的节奏,消费消息队列内的数据就行了。
<img src="https://static001.geekbang.org/resource/image/86/b9/86cf475dda27dcc25758b976e4d935b9.jpg" alt="">
另外,消息队列还提供了额外的两个好处:
1. 实现了 MQTT Broker 服务器和数据流处理服务器之间的解耦,双方没有直接的依赖,所以维护更新会更加方便。
1. 可以平衡输入数据量的大小变化,所以数据流处理服务器不会因为骤增的压力而崩溃。
既然消息队列这么重要,你肯定也想知道,在实践中有没有开源的软件可以选择呢?答案是有的。最常见的选择有**Kafka**和**RabbitMQ**等。
Kafka真是一个神奇的软件凭借着优秀的设计成为消息队列系统的主流选择。而且在这个成功的基础上它还在不断改进新增了分布式流处理和分布式存储等功能。虽然这些新功能不一定比专门的流处理和存储软件更强大但对于中小型的业务来说完全够用了关键是非常方便部署也简单。
## 缓存系统:让数据读写更快速
从架构图中我们可以看出来,数据流处理服务器处理完数据后,这些数据会存储到数据库中,提供给批处理或者业务服务器使用;而且它在进行数据处理时,也需要从数据存储中获取一些信息。
但是数据库的读写数据是一个速度比较慢的操作,尤其是基于磁盘介质存储的数据库。为了提高性能,我们需要比数据库,甚至分布式文件系统更快的数据存取方式,这就需要用到**缓存系统**了。
缓存是一个常见的概念。比如浏览器会使用缓存来避免重复从网络获取网页数据,从而更加快速地响应用户的请求。
在我们的系统中,引入缓存系统当然是为了避免直接从磁盘中读取数据,或者直接向磁盘中写入数据。
缓存系统一般会将数据暂时存储在内存中,这样数据流处理应用就不需要直接与低速的磁盘打交道了。而且,如果我们读取的是经常用到的热点数据时,这些数据全都不需要重复从磁盘读取。这样既减轻了数据库的压力,又提高了数据处理速度,一举两得。
缓存系统的常用开源选择有**Redis**和**Memcached**等。其中Redis更是在数据持久机制和主从节点复制的高可用特性上做了很多工作不但功能强大而且效率也很高。
加入缓存系统之后,设备接入层的整体系统架构就完成了,你可以参考下面这张图。
<img src="https://static001.geekbang.org/resource/image/11/88/11ef8db8026f1d9578d9c974a3af8b88.jpg" alt="">
## 小结
总结一下,物联网云平台的服务器为了应对海量设备接入和海量数据输入的挑战,需要打造高并发的设备接入层。所以在这一讲中,我带你设计并完善了设备接入层的整体架构,并讲解了需要用到的**负载均衡**、**消息队列**和**缓存系统**等技术。
1. 负载均衡用来协调多台服务器来共同应对网络连接和请求的压力,服务器任务分配策略主要有三大类,分别是平等分配的轮询策略,考虑连接设备数的最小连接策略,以及保证相同的源地址访问同一台服务器的源地址哈希值策略。
1. 这些策略可以搭配使用还可以通过引入加权系数来改进。在实际应用中我们需要根据场景灵活选择常用的开源方案有HAProxy 软件和 Nginx软件部署时可以搭配Keepalived提高可用性。
1. 消息队列可以在不同的系统之间搭建桥梁保证数据和服务请求的高效可靠处理。常用的开源软件有Kafka和RabbitMQ等。
1. 缓存系统可以减轻数据库的压力提高系统响应速度。常用的开源软件有Redis和Memcached等。
## 思考题
最后,我想请你思考一个问题。
在这一讲中我们通过负载均衡来分担服务器的压力。这里设备是使用MQTT协议与接入层服务器也就是MQTT Broker服务器通信的所以不同的设备发送的相同 Topic 的消息就会发送到不同的服务器上。
如果有订阅者订阅这个 Topic 消息那么应该怎么保证订阅者可以接收到所有的设备发送的此Topic的消息呢
欢迎你在留言区写一下自己的思考,同时也欢迎你将本讲分享给对高并发感兴趣的朋友一起讨论学习。

View File

@@ -0,0 +1,191 @@
<audio id="audio" title="13 | 隐私:在实践中如何保护用户隐私?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/0f/36/0ffea16a43eaebda9d37fcac65d94536.mp3"></audio>
你好,我是郭朝斌。
在前三讲中,我带你完善了对物联网系统的**数据技术体系**的认知,包括处理框架、存储方式和设备接入等。
这些内容涉及到很多**大数据技术**,比如基于数据分析结果,提前调度共享单车的位置;还有基于你的搜索记录和购买历史,向你推荐商品。
在**大数据时代**,还有一个话题也是我们非常关心的,那就是这一讲要说的**数据隐私**。这个话题涉及的技术知识点不多,所以你学起来会相对轻松一些。
## 为什么要了解隐私?
不过,你可能还是要问了:在这个技术类的课程中,为什么要讲这个跟技术关系没那么大的内容呢?作为开发人员,有必要了解这些吗?
其实这样安排有三个原因。
**第一,面对产品的需求和设计时,我们最好能做到“知其所以然”。**
因为只有这样,才能更好地审视需求,在完成工作的同时,也能提升对业务的理解。
比如,你在手机上新安装了一个应用,打开之后很可能会看到一个隐私条款的弹框。你可以参见下面的图片:
<img src="https://static001.geekbang.org/resource/image/45/dc/454908729ee318405621a9a3f2b32cdc.png" alt="">
当产品经理向你提出需求,让在你开发的产品中增加这个弹框的时候,你可能会想:为什么要多此一举,设计一个只能同意的隐私条款弹框呢?难道产品同学今天没休息好,怎么关于用户体验的经验还不如你?但是学完这一讲,你就会知道,其实这是隐私法律的要求。
**第二,正因为相关的法律法规已经施行,我们需要了解和遵守法律法规,保护用户的隐私数据。**
一方面,这要求你在系统开发中采取必要的措施保证数据的安全,比如对数据进行脱敏处理;另一方面,虽然你可能接触到非常多的用户数据,但一定不能随便泄露数据,因为这是违法的。
类似的情况还有金融圈的P2P网贷。当初很多开发人员不懂法网贷公司跑路之后他们却糊里糊涂地遭受了牢狱之苦连之前的工资收入也被没收。
近几年,也已经有很多人因为隐私案件而遭遇类似的经历。比如某公司通过违规收集简历,进行个人数据的违法利用,当公安进行查处时,公司所有员工都被牵扯进来。
另外,如果你的产品要出海,那么国外重要市场的隐私政策也得处理好。因为有些国家的法律法规比较成熟,仅仅是数据防护不力就会导致巨额的罚款。
**第三,物联网的应用领域(比如智能家居)可能是一个大生态系统,我们也要满足对接平台的政策要求。**
我们开发的产品可能要对接到小米等平台上。这些平台一般都有严格的隐私政策(这些政策可能比法律法规的要求更严格),隐私方面的合规检查是产品上架流程的重要一环。这就要求我们对相关政策要求做到心中有数,在产品开发过程中,能够提前做好隐私数据的规划。
## 什么是隐私数据?
现在你可能要问了:要求这么多,是不是数据相关的功能和应用都不能做了呢?可是为什么我在电商平台上,还是可以看到很多商品推荐信息呢?
这就涉及到对**隐私数据**的定义了。到底什么是隐私数据,什么不属于?在实际的工作中,如何把握这个度呢?只有搞清楚这个关键问题,你才能更好地把握隐私法规,做好数据应用工作。
我先举个例子,你来判断一下。比如,一些平台会收集你的历史消费信息、生活的城市和使用的手机品牌等数据,构建你的“用户画像”,然后给你贴一些标签。
你要是被贴上了“一线城市”“高收入”和“消费频繁”等标签,它们就会觉得你对价格的敏感度低,对平台的粘性高,最后把同样的商品以更贵的价格卖给你。
请问这触犯了隐私法规吗?当然,这其实就是所谓的**“大数据杀熟”**。2018年它甚至被评为年度社会生活十大流行语引起了人们的广泛关注你我可能都是受害者。
大数据杀熟和商品推荐系统一样,都利用了**用户画像**。但是商品推荐系统是不违法的,在实践中有很多应用,而“大数据杀熟”却被明确禁止,这是为什么呢?
它们的区别主要在于,“大数据杀熟”针对的是**特定的个人**。当个人被区别定价、达成交易时,这就是对特定个人的**数据滥用**。
**个人信息**(或者说个人数据,不同国家的叫法不同)可以分为**个人隐私信息**和**个人一般信息**隐私法律法规主要关注的是个人隐私信息包括身份证件号码、个人生物识别信息、银行账户、通信记录和内容、财产信息、征信信息、行踪轨迹、住宿信息、健康生理信息、交易信息以及14 岁以下(含)儿童的个人信息等。
另外,通过个人一般信息和其他信息的加工处理形成的信息,一旦泄露、非法提供或滥用,可能危害人身和财产安全,或者导致个人名誉等其他损失的,**也属于个人隐私信息**。比如,有人通过一个明星在微博上发布的图片,推测出明星的住址。
而在万物互联的物联网时代,隐私泄露的风险更大。因为各种设备收集的数据都会上传到云平台,数据的维度和数量都比互联网应用更加丰富,比如你身体的各项生理指标、汽车的行驶记录和家里智能摄像头的影像等等。
同时,现在大数据处理技术、挖掘算法,更是可以基于这些对你来说非常敏感的信息,得出其他的敏感信息。比如你的驾驶习惯好不好,经常开车去什么地方,什么时间出入停车场,汽车保险公司肯定非常想知道。
面对这样的趋势,谁也不希望成为隐私泄露的受害者,变成一只“待宰的羔羊”。所以用户自然也会更加注重数据安全,甚至把这一点作为选择物联网产品的关键决策因素。我们国家也在加强隐私相关法规的完善,规范企业在个人信息利用中的行为。
于是,隐私保护的诉求给物联网的产品和服务提出了更高的要求。那么作为产品的设计者和开发者,你在实践过程中应该怎么保护用户的隐私呢?
目前,你在工作实践中,首先要做到的就是隐私合规,也就是满足各个国家和地区的相关法律法规的要求。因为这是硬性要求,是第一步,只有这样,你的产品才可以顺利地销售或者提供服务,否则就要面临下架和罚款等处罚措施。
## 国内隐私法律法规
我们先关注一下国内的隐私法规情况。
这一讲开头提到的隐私条款弹框,其实就是《信息安全技术 个人信息安全规范》的要求。它是对《网络安全法》的补充,是国内第一个关于个人信息的实践标准。
有法可依之后公安网安部门已经针对App等网络服务进行了多次个人信息违法问题的[整治行动](https://www.aqniu.com/industry/67527.html)其中就有像联网汽车App这样与物联网有关的案例出现。
另外我国也在积极推进个人信息保护法的制定。在2020年10月13日《个人信息保护法》草案已经提交到人大常委会讨论。也许在不久的将来就会有正式的法律落地。
<img src="https://static001.geekbang.org/resource/image/45/15/45a16b98db6ef32c6348c9d27cfb5915.jpg" alt="">
## 出海要注意什么?
看完国内的法律法规,我再来介绍一下国外的情况。
国内企业的出海是越来越明显的趋势,尤其物联网行业里面的智能硬件企业,他们借助我国完整的产业链,具有得天独厚的优势。
在出海过程中,关注国外的隐私法律就成为必须做的事。在实践中,你需要重点关注的是几个主要市场的要求。
### 欧盟GDPR
我们先看一下欧盟。欧盟的[GDPR](https://stripe.com/zh-cn-us/guides/general-data-protection-regulation)General Data Protection Regulation通用数据保护条例知名度非常高全世界都在关注。它对个人数据进行了全生命周期的保护被称为**全球最严苛**的隐私政策。
在2018年GDPR生效之后欧盟马上就依据**数据跨境传输**等原因给以Google为代表的美国企业开出了5000万欧元的罚单。GDPR不仅适用于当地公司而是对所有在欧盟提供的产品和服务的公司都提出了限制。它要求个人数据必须落地在欧洲的服务器存储不能随意传输到其他地区。
### 美国:《加州消费者隐私法》等法案
接着我们再看看美国的情况。美国比较特殊,它是海洋法系,系统法律条文不多,主要靠援引之前判例,而且每个州的自主权也比较大。所以整体来说,美国隐私相关的法律法规是散落在各个州,或者金融、保险、医疗和电信等领域的各种法案当中。
其中信息产业发达的加州在2018年6月生效了《加州消费者隐私法》California Consumer Privacy Act这是目前美国最全面、最严苛的隐私法规。
另外美国也有人在推动全国范围隐私法规的建立。比如苹果的库克就建议制定完整的联邦隐私法当然这对于Google和Facebook这些企业是不利的因为这些企业的服务更依赖个人信息。
### 出海需要重点关注的法律法规
总体来说,每个国家和地区都有自己的法律体系和发展节奏,而且法律工作本身是一件非常专业的事情。所以你在出海过程中,最好找**靠谱的、有经验的咨询机构**合作,保障产品在法律上不出问题,避免不必要的经济损失。
我整理了一个表格,列举了在个人信息方面,你需要关注的主要国家和地区的法律法规,供你参考。注意,这里“个人信息”“个人数据”和“隐私”三个名词意思差不多,只是不同地方的表述习惯不一样。
<img src="https://static001.geekbang.org/resource/image/6e/84/6eb8a7d0244e27ce0ded9b554bc5dc84.jpg" alt="">
## 怎么把握繁多的法律法规?
法律法规的内容非常多作为行业从业人员我们也不可能把所有条文都背得滚瓜烂熟。那么有什么简单的办法来指导日常工作吗我想你在实践中可以把握下面这4个基本原则。
**第一,保证用户有知情权和选择权。**
如果需要收集用户的个人信息,我们需要告知用户,征求用户的授权,而且要用显著的、容易懂的方式说明信息的使用方式、使用范围和目的。
比如我刚才提到的隐私信息弹窗就是一个例子。而且用户还要能够决定是否同意授权以及授权之后再取消授权。你在做智能硬件配套的App时可以留意这方面的要求。
**第二,收集、使用信息时要采用最小必要原则。**
这个比较好理解。我们收集使用的信息应该尽量少只要能够满足产品和服务的功能需求就足够了。比如智能台灯我们只需要接收的房间信息就可以实现语音控制和交互并不需要收集准确的GPS定位。
**第三,保证用户有信息控制自主权。**
我们收集的信息的主体仍然是用户也就是说用户随时可以查看自己的数据可以修改、甚至删除它们。你可以在App或者网页中提供这样的入口。
**第四,确保个人信息存储和使用的安全。**
收集了用户的个人信息,我们就需要保证个人信息的数据安全,从内部人员的管理和数据的使用流程上把控风险,比如将数据中的个人指向性内容去掉。同时,我们也要做好系统的安全防护工作,防止被攻击导致数据泄露。在下一讲中,我还会讨论更多关于系统安全的内容。
从这些原则出发,你就可以更好地理解和满足相关法律法规的要求,做好产品和服务的合规工作。
## 从用户的角度出发的设计
以上内容主要是从法律角度来讨论这个问题。但是从根本上说,你还是需要从用户的角度出发,在为用户创造价值和解决问题的同时,保护好用户的隐私。
法律永远是滞后的,法律也总有漏洞,各种利用个人隐私信息获利的新方法会不断出现。但是用户会“用脚投票”,选择隐私保护更好,更值得信赖的产品和服务。从这个角度考虑,隐私保护虽然需要企业投入成本,但是它也可以成为企业独特的竞争优势。
比如国外有一个搜索引擎公司DuckDuckGo在棱镜门事件后日均使用量一年就翻了一倍近几年用户量也是稳步增长2020年8月用户量就达到了6500万。它吸引用户的一个关键点正是强调用户隐私不追踪用户也就是**匿名性**。这和Google这样的搜索引擎恰恰相反。
除了匿名,你还可以像苹果公司一样采用“**差别隐私**”的方法,通过在数据中增加“噪音”,或者修改数据隐私信息,来保护用户的个人信息泄露。
我再举一个互联网应用的例子,因为你日常中接触得更多。
当你在微信中分享照片,无论通过聊天界面还是朋友圈,微信一般都会对照片进行压缩。有时候你可能还会抱怨微信把照片处理得不够清晰,所以专门选择发送原图。
其实微信在压缩照片的同时,都会修改照片的**EXIF信息**,它包含拍摄时间、地点等隐私信息。如果你的照片被“有心人”利用,他们可能通过数据分析,直接精确地定位到你家小区,是不是很可怕?
其实微信还有很多类似的从用户角度出发的设计。比如微信朋友圈中的“给谁看”“不给谁看”“仅自己可见”等,就是**给用户控制数据可见范围的选项**。这样用户会更有控制感。
另外,可以**减少信息的展示时间,甚至避免信息的永久存储**。最知名的就是聊天软件上的“阅后即焚”功能。还有微信中聊天记录不会被云端永久保存,朋友圈中的“三天可见”“半年可见”,也是同样的设计思路。这种设计可以让用户更放心地分享内容,提高用户体验的同时,也增强了产品的粘性。
那基于同样的思路,在硬件上我们也可以多采取类似的产品设计方法和角度,提高用户的信任感。
比如,智能手表设备会收集很多用户的个人生理指标数据,而且一般在设备上有数据的存储功能。这种存储当然出发点是为了缓存数据,保证数据在手机或者云端的可靠获取,但是它也存在泄漏的风险。
那我们就可以增加一个功能,让用户可以随时通过手表本身,不用借助手机等其他设备就可以永久删除存储的历史数据。这样的设计会让用户更加放心地使用产品。
## 小结
总结一下,在这一讲中,我分析了学习隐私知识的必要性,重点讲解了隐私数据的概念,并介绍了国内、国外关于用户隐私的法律法规和把握这些法规的基本原则。
除了法律法规,我也谈了从用户角度出发,做好隐私保护工作的思路和方法。了解了这些,你才能在物联网行业从业过程中做好合规工作,并且设计出让用户真正感到满意和放心的功能。
1. 从懂业务、懂法律和懂生态的角度,我们作为开发人员都需要掌握隐私的相关知识。
1. 在隐私法规上,个人信息可以分为个人隐私信息和个人一般信息。我们需要重点关注个人隐私信息的保护。
1. 在隐私保护方面我国已经生效的法律有《网络安全法》和《个人信息安全规范》。另外《个人信息保护法》的草案已经提交人大常委会讨论在不久的将来应该也会正式发布。企业出海时需要关注当地的法律比如欧盟的GDPR以及美国各个州和各个领域的相关法案。为了保证出海工作的顺利进行我们最好还是找专业的咨询机构合作。
1. 作为物联网行业的从业者在实践中要把握4条基本原则第一保证用户有知情权和选择权第二收集、使用信息时要采用最小必要原则第三保证用户有信息控制自主权第四确保个人信息存储和使用的安全。
1. 让产品和服务符合法律法规的要求只是第一步。在产品理念,或者说底层价值观上,我们要从用户角度出发,重视对用户隐私的保护。具体的方法有增加匿名性,采取差别隐私,让用户可以控制数据可见范围和减少信息的展示时间甚至避免永久的存储。
我整理了一张思维导图,供你参考。
<img src="https://static001.geekbang.org/resource/image/dy/a5/dyycc0473f2c1d8bc70fecef547dafa5.jpg" alt="">
隐私政策在近几年备受关注,这背后的本质原因,也许是科技的发展、数字化深入生活的必然趋势。用户只有让渡一些个人信息才能获得更好的服务,或者更有利于需求的解决;但是任何时候,用户的个人信息都不应该被滥用、被泄露、被非法提供进行交易。
正如张小龙说的那样:“**善良比聪明更重要AI比我们更聪明、更懂套路但我们可以比他更善良。**”只有这样用户才会信任你,你也才能拥有一个长久的、有生命力的产品和服务。
而如果作为消费者,我们也需要了解这些法律法规的基本信息,保护好个人的隐私数据,并且遇到问题时,把它们作为维护自己权益的工具。
## 思考题
最后,我给你留一个思考题吧。
智能家居中,用于安全监控的摄像头有很多的价值,但是很多人担心它的安全性,害怕隐私数据的泄露。如果你来设计摄像头产品,从用户的角度出发,会做哪些设计来增加产品的认可度呢?
欢迎你在留言区写一写自己的思考,也欢迎你将这一讲分享给对隐私保护感兴趣的朋友一起学习。

View File

@@ -0,0 +1,211 @@
<audio id="audio" title="14 | 安全:物联网平台如何应对安全风险?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/f5/12/f5474ff69042539ab36f469dbab0b812.mp3"></audio>
你好,我是郭朝斌。
在[第13讲](https://time.geekbang.org/column/article/317861)的思考题中,我提到了用户对于摄像头泄露隐私的担忧。这样的顾虑并不是杞人忧天。在现实生活中,物联网设备确实面临很多安全问题,而且这些安全问题已经对个人信息安全构成威胁。
比如2019年12月在美国非常流行的家庭安防产品[Ring 爆出安全漏洞](https://www.wired.com/story/ring-hacks-exemplify-iot-security-crisis/)。黑客不但可以远程查看Ring的监控视频还获取到了家庭Wi-Fi的密码甚至可以把收集的用户音频数据分享到开放的播客节目中。
作为物联网开发人员或者安全技术负责人,我们肯定不想看到自己负责的产品和服务上负面新闻。为了跟用户建立信任,我们必须切实做好安全工作,打造可靠的产品和服务,保护数据不被泄露。
你可能已经对于互联网的安全工作有一些了解,甚至有一定实践经验。不过在物联网领域有很多新的变化,所以我们需要重新思考安全工作该怎么开展。
在物联网设备上,安全的实践并不像在电脑上那么成熟。虽然现在的公司也越来越重视设备的安全问题,但是还是经常出现失误。
更何况,很多设备都是创业公司或者外包团队研发生产的。它们本身的盈利能力还比较弱,没有足够多的资源投入到安全方面,所以这些设备面临的安全问题往往更加严重。
总的来说,安全问题主要反映在两个方面:第一,公司没有针对物联网的特点建立起完善的安全管理流程和制度;第二,在具体的技术实践中,没有考虑物联网面临的新的安全问题。
所以在这一讲中,我就从管理和技术两个方面出发,跟你分享一些物联网的安全实践经验。
## 安全管理规范
首先,你需要建立一个**安全管理规范**,从机制和组织上保障安全,不能因为各方面资源的限制而放松要求。
从实践的角度出发,我把安全管理分为**开发**、**生产**、**部署**和**运营**4个方面。
### 1. 开发:基于安全开发生命周期建立流程
开发是产品和服务的基础,是最关键的地方,所以我先说说开发。
在开发过程中,怎么保证安全,满足合规性的要求呢?你可以基于[**安全开发生命周期**](https://dzone.com/articles/how-to-approach-security-development-lifecycle-sdl)SDLSecurity Development Lifecycle来建立企业的安全开发流程。SDL早已是保证开发安全的公认方法论。它能够减少安全漏洞的数量、降低安全漏洞的危害从而帮助你做出更安全的产品和服务。
SDL的整个周期由六个过程组成我总结在了下面这张图片里。
<img src="https://static001.geekbang.org/resource/image/9f/18/9f7a824790ff0c5783bf89b1426a1c18.jpg" alt="">
### 2. 生产:降低由工厂带来的安全风险
跟纯软件产品不同,物联网设备还需要在工厂生产。那么,在生产中你要注意什么安全问题呢?注意,这里的“安全”不是指**人身安全Safety**,而是指**出厂设备的安全Security**。
首先,你在选择合作工厂的时候,要确保对方具备一定的**质量体系流程**,最好有第三方的质量体系认证。这样可以一定程度上保证工厂在流程上的安全工作。
其次,你在设计产品的时候,一定尽量**减少在工厂进行涉及安全的操作**,比如设置安全秘钥等。这样可以最大限度地减少工厂操作失误和数据泄露带来的问题。
最后,还有一些有安全风险的操作是必须在工厂进行的,比如设备固件烧录和硬件电路板组装等。对于这些操作,你要和工厂一起确定好流程,做好固件版本和硬件批次的控制,同时在**成品质量检测**中增加软、硬件版本的检查项目。
### 3. 部署:从软件和硬件两方面考虑
在物联网产品和服务投入正式使用之前,我们还需要进行软件和硬件的部署工作。为了保证系统的安全,你需要采取哪些措施呢?
一方面,你要做好**软件**的安全部署。
在服务器上部署软件的时候,你要检查防火墙的配置,保证不必要的端口都是关闭的。另外,软件的各种安全设置都要打开,不能让安全设计沦为摆设。
另一方面,你也要做好现场的**硬件**安全部署。
在现场部署硬件设备的时候,你需要考虑设备被随意获取、暴力拆解和分析的情况。因为拿到设备进行研究,往往是对物联网设备进行安全攻击的第一步。尤其是那些不能从商场买到的,或者部署在室外的非普通消费品,比如智能电表,还有无人值守停车场部署的计费设备等。
所以你得采取一定的**物理防护措施**,比如安装监控摄像头或者额外的防护装置(比如外壳等),让攻击者不能轻易地获取到设备。
另外,在设备初次入网或者重新入网的时候,你要保证**操作人员的可信度**,并做好**安全引导工作**。同时,你必须要求操作人员把设备的一些控制接口关闭,或者修改初始默认密码。比如对于网关设备,你可以关闭本地管理界面功能,或者修改初始的密码,这样可以避免被攻击者轻易地破坏。
### 4. 运营:把安全融入持续的运营过程中
最后在产品和服务运营的过程中我们还需要做好监控、运维和应急响应组织建设这3个方面的安全事项。
**监控**可以保证及时发现问题,是非常重要的运营手段。
比如,你可以通过系统实现**对设备和平台系统的日志进行自动化监控和分析**。这可以帮助你从一些记录中(比如系统尝试提权和认证异常等)及时发现异常行为。
还有一些监控发现的事件,是需要及时通知用户的。比如异常登录,系统要及时通过短信或者邮件告知用户风险。
**运维**是确保服务和设备安全状态的重要工作。
除了做好云平台服务器的运维工作之外,你还可以在系统中**增加设备资产的管理功能**,这有利于跟踪设备状态和固件更新等工作。
另外,很多物联网设备中存储有秘钥、证书等敏感信息。所以对于所有被淘汰的物联网设备,你都要做好**退役期处理**,把关键信息从设备上擦除掉,同时在云平台上对设备做归档、标记等处理。
**应急响应中心**是互联网公司在长期的安全实践中做出的非常好的创新。
在互联网发展的初期,一些“白帽子”黑客就算发现问题,也很难流畅地反馈给企业处理,甚至会被认为是勒索。但目前主流的公司基本上都会设置安全应急响应中心,甚至鼓励在一定的范围内**跟白帽子合作**,来提高系统的安全性。这是你在实践中值得借鉴的方法。
## 安全技术保障
刚才说的安全管理规范,只是从制度上来减少安全漏洞和安全事故,并对安全事件进行及时响应和处理。但是实际工作中,你还需要从技术上来保障安全。
怎么做好技术保障呢?我们知道物联网从架构上可以分为三层,设备层、网络层和应用层,你可以从这三个层面入手来做好安全工作。
现在我以共享单车为例做一个讲解。
### 第一步:分析安全需求
共享单车,你我都很熟悉,我们很容易就能分析出它的安全需求:
1. 保证单车资产的安全
1. 确保开锁、计费等关键功能和数据的完整性
1. 保证个人信息、轨迹信息等敏感信息的保密性
1. 确保云平台、App等整体系统的可用性
接着,我们细化这些需求,列出明确的指标,然后跟公司的安全基线对比,看是否符合安全基线的要求,对于不符合的部分是否可以放行。
公司的安全基线是从哪里来的呢?一是公司自己的实践经验,二是最佳安全实践指导文件。
这些指导文件来自安全公司或者安全组织比如国内的绿盟国外的CSACloud Security Alliance云安全联盟、IICIndustrial Internet Consortium工业互联网联盟和 IoTSFIoT Security Foundation物联网安全基金会等。
另外,还有一些行业相关规范涉及的安全内容,包括我在[第13讲](https://time.geekbang.org/column/article/317861)介绍的隐私相关法律法规对于安全防护的要求,这也是公司安全基线的一个来源。
### 第二步:构建威胁模型
明确了安全需求之后,下一步就是构建共享单车系统的威胁模型。建模可以帮助我们全面地了解共享单车系统中各个角色、接口和数据流,然后分析清楚系统面临的攻击面和信任边界。基于攻击面和信任边界,我们就可以更好地识别系统的潜在威胁。
至于建模的具体方法我们可以使用OWASP提出的[威胁建模备忘清单](https://cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html)。它的好处是有一个开源的工具可以使用,名称是[Threat Dragon](https://github.com/OWASP/threat-dragon-desktop/releases)。除了这个,我们还可以选择[微软的威胁建模工具](https://www.microsoft.com/en-us/securityengineering/sdl/threatmodeling),它也是免费的。
首先,我们基于共享单车系统的组成绘制出数据流图。下图是我使用 Threat Dragon 工具创建的共享单车系统的数据流图。(注意,这里只是举例,现实中的单车系统可能与此不同。)
<img src="https://static001.geekbang.org/resource/image/54/d4/546590e6e763afaba28e53f28bfe3fd4.png" alt="" title="共享单车数据流图">
基于数据流图,我们就可以做威胁识别了。你可以借助微软的[STRIDE模型](https://www.ockam.io/learn/blog/introduction_to_STRIDE_security_model)来完成这也是Threat Dragon支持的威胁定义方法。
STRIDE模型将威胁分为六种不同的类型欺骗、篡改、否认、信息泄露、拒绝服务和权限提升它们在下面的威胁识别表格中都有体现。
<img src="https://static001.geekbang.org/resource/image/eb/4f/eb7968cf5d06d721a6bca36bde7f004f.jpg" alt="" title="共享单车威胁识别表">
识别完威胁以后,我们需要归档,然后根据发生概率和可能的危害程度,对它们进行评估。评估可以使用微软的[DREAD模型](https://en.wikipedia.org/wiki/DREAD_(risk_assessment_model))来完成。它按照5个维度对一个威胁进行风险等级打分分值范围110然后计算出平均值作为威胁的最后分数分数越大风险越高。
对破坏车体二维码的威胁进行DREAD风险评分可以得到表格所示的结果。
<img src="https://static001.geekbang.org/resource/image/7f/ed/7f910286987061973ec31c7632d4c9ed.jpg" alt="" title="破坏车体二维码的威胁风险评分表">
基于威胁评估的结果,我们可以对威胁的安全控制措施进行优先级排序,形成安全设计文档。然后我们把安全设计文档中的控制措施加入到产品的需求列表中进行跟踪,有计划地实施。
下面是我得到的共享单车系统的威胁矩阵,我按照物联网的三层架构进行了分组。
<img src="https://static001.geekbang.org/resource/image/a2/de/a2867817b97ffa287dddde2c71f8c2de.jpg" alt="" title="共享单车威胁矩阵表">
接下来,我就分别讲解一下三层的安全控制措施。
### 第三步:逐层技术防护
#### 设备层安全:业务目标的关键
首先是设备层。由于共享单车大多处于无人监控且恶劣的环境中,设备层的安全风险较为突出。
对于谎报电子锁故障我们一方面可以在App中增加证据上报功能比如图片、视频等来增加谎报的难度另一方面可以在电子锁上增加更准确和可靠的状态检测传感器并且将这些信息上报服务器这样就可以实现攻击识别的自动化让攻击者不敢轻易尝试。
针对固件的攻击行为,实施成本较高,但对于专业人员来说也不是太大的难题,所以我们需要做好以下几个方面的防护措施:
1. 采取物理防护措施,加大拆除电子锁和拆解锁体的难度,让攻击者无法轻易获取到电子锁。
1. 采取篡改检测机制,通过传感器(比如压电薄膜)来检测破坏行为,然后声音报警,并且上报事件到云平台。
1. 在可能的情况下尽量不启用芯片的调试接口比如JTAG和串口从而防止攻击者获取固件文件和控制接口。
1. 避免将敏感信息(比如加密私钥)硬编码到固件中,以防攻击者从硬件中获取到。
对于刚才提到的破坏二维码行为,最直接的方法当然是增加攻击难度,比如在车体多处增加二维码信息,采用更坚固的材料保护二维码。
不过根本的办法还是寻找其他技术方案来替代二维码或者作为二维码的备用方案比如NFC和蓝牙技术。
#### 网络层安全:加密通信是根本
然后是网络层。网络层面临的是不可控的传输网络,和未正确使用的通信技术,所以在物联网上传输的数据包如果没有加密和签名,很容易发生被窃听、篡改、伪造以及发送者抵赖等问题。
在2017年的[GeekPwn大会](https://m.sohu.com/n/494029756/)上就有安全人员通过截获Wi-Fi网络上的数据包轻松地获取了别人在4款共享单车App上的账号权限。他们靠这些权限不但可以开锁还能掌握用户的GPS轨迹等信息。这跟上面表格提到的通过手机App获取账号和伪造电子锁发送关锁信息属于类似的实现方式。
所以我们需要对网络通信进行加密,采用**SSL/TLS**的方法进行安全通信。在App上可以采用 SSL Pinning的模式进行双向认证保证通信双方传输交换安全电子锁设备的通信也要加密可以采用MQTT支持的SSL/TLS方式。
另外,蓝牙协议本身就支持很多安全机制,能够实现配对安全、数据防篡改和设备认证等特性。现在很多共享单车除了支持服务器下发命令开锁以外,还支持采用蓝牙协议来开锁。这时候一定要记得使用蓝牙的数据加密机制,保证开锁命令不会被攻击者轻易获取。
#### 应用层安全:保证可用性和数据安全
最后是应用层。它作为物联网业务服务平台,向用户提供相关业务及应用。同时,在这个过程中,它还会采集、存储和处理大量的敏感数据。所以,我们必须保证服务的可用性和安全性。
**DDoS攻击**可以说是目前最难防御的攻击之一还没有特别完美的解决方案。我们可以采取多种方案组合结合攻击的实际情况灵活应对达到减少损失的目的即可。常用的手段有增加带宽通过防火墙清洗过滤异常流量以及与专业的提供抗DDoS服务的公司合作。
对于上面威胁矩阵中提到的防范恶意植入和SQL注入我们需要做好下面几件事
1. 及时更新服务器操作系统和其他软件,尤其是出现严重漏洞的软件,不给攻击者可乘之机。
1. 设置防火墙,同时关闭不需要的服务和网络端口。暴露越少,被攻击的可能性就越小。
1. 定时对服务器和数据库做好备份,避免重要数据丢失或者服务不能及时恢复的情况。
1. 做好系统安全监控,及时发现异常的攻击行为,为进一步的安全防护做好准备。
另外服务器对外提供的开放接口也可能导致的SQL注入等安全问题同样需要注意。对于这些问题我们可以通过定期做渗透测试来防范。
## 小结
总结一下,在这一讲中,我从安全管理规范和安全技术保障两个角度,讲解了我们在安全上需要关注的地方。
首先,我们要建立安全管理规范,从机制和组织上保障产品和服务的安全,主要分为四个方面:
1. 开发方面我们可以基于SDL来建立企业的安全开发流程保证开发符合安全规范。
1. 生产方面,通过质量体系、安全检查等保证出厂产品的合规。
1. 部署方面,既要注意软件的安全配置,也要保证硬件现场部署的安全措施落地。
1. 运营方面,我们需要做好监控、运维、应急响应组织建设这三个方面的安全事项。
其次,实际工作中,我们还需要从技术上来保障安全。具体步骤是先分析安全需求,再构建威胁模型,然后基于威胁建模的输出,在物联网三层架构中分别做好技术防护措施:
1. 在设备层中,做好固件、硬件接口上的防护工作。
1. 在网络层中重点关注通信技术、通信协议的风险保证网络通信采用SSL/TLS 加密方式。
1. 在应用层中做好DDoS攻击、SQL注入等安全防护及时处理安全漏洞。
我整理了一张思维导图,供你参考。
<img src="https://static001.geekbang.org/resource/image/b8/3c/b84128760779b4a245075e5e47008d3c.jpg" alt="">
从用户的角度看,安全和隐私可以说是密切相关。用户担心产品的安全问题,主要关注点也是害怕个人信息的被泄漏和滥用。作为从业人员,我们只有做好安全工作,才能落实好个人信息的保护,从而打造出安全可靠、用户信任的产品和服务。
## 思考题
最后,我给你留一个思考题吧。
我在[第7讲](https://time.geekbang.org/column/article/311616)介绍零配置组网时提到了UPnP协议。它为我们日常使用设备提供了便利但也被称为“安全重灾区”。你知道UPnP协议存在哪些安全风险吗
欢迎在留言区写下你的答案和我交流,也欢迎你将这一讲分享给对物联网安全感兴趣的朋友,大家一起讨论学习。

View File

@@ -0,0 +1,262 @@
<audio id="audio" title="15 | 平台:智能家居开源平台的生态是怎样的?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d1/4e/d12683d10a704098fb70586a0416f54e.mp3"></audio>
你好,我是郭朝斌。
在学完了这门课程的前十几讲之后,你已经对物联网有了一个从整体到细节的全面了解。我想,你现在应该迫不及待地想要动手实践了吧?
在正式进入实战篇之前,我想以智能家居领域为例,带你了解一下开源平台。
## 智能家居:生态开放
为什么选择智能家居这个领域呢?最重要的原因当然是因为实战篇的动手项目是智能家居领域的。而另一个原因是,智能家居的生态环境比较开放,很有活力,而且也更贴近我们的日常生活。
在[第9讲](https://time.geekbang.org/column/article/313631),我提到过和工业物联网相关的一些开源项目。相比之下,工业物联网领域更加封闭,如果不是行业从业人员,你很难理解它的功能需求。
那么,什么是平台,平台有什么用呢?就拿智能家居平台来说好了,它给各种智能家居产品提供了一个**统一的接入系统**,让用户可以**管理**和**控制**产品,同时也为各种智能家居产品之间的**联动**提供了条件。
如果没有智能家居平台,那么各种智能家居产品就是一个个孤立的单品,既不能远程控制,也不能实现联动功能。
如果你买过小米的智能产品应该接触过小米的米家平台它就是一个典型的智能家居平台。国内类似的还有阿里巴巴的天猫精灵、京东微联和海尔U+等。国外知名的智能家居平台有苹果的Apple HomeKit、亚马逊的Amazon Echo和谷歌的Google Home等。
## 开源平台:方便定制
不过,刚才提到的这些都是商业公司运营的平台,它们使用的技术和通信协议都是公司制定的,能够选择的产品也只能是已经接入平台的产品。作为开发实践者,我们可以定制的部分非常少。
所以在这一讲,我想介绍一下智能家居的开源平台生态。
一方面,开源平台能为你提供一个现成的基础,你可以在这个基础上快速地搭建出自己的智能家居系统。而且这些平台不但可以提供丰富的特性,一般也会提供各种定制能力,方便你基于自己的需求灵活地调整。
另一方面,就算你在工作中不直接使用这些开源平台,你也可以从它们的架构设计和实现逻辑中汲取灵感。
在介绍完智能家居的开源生态之后,我还会选择其中一个开源平台,带你在树莓派开发板上运行起来,这样你也可以初步体验一下智能家居软件。
## 热度比较高的开源平台
那么你觉得智能家居的开源平台多吗?我告诉你,其实非常多。这也从侧面反映出智能家居是一个非常被看好的领域。
在这一讲中把所有的开源项目都讲一遍是不现实的所以我精心挑选了5个热度比较高的项目介绍供你参考。
<li>
<h4>Home Assistant</h4>
</li>
首先要说的就是[Home Assistant](https://www.home-assistant.io/)。由于支持丰富的硬件和软件组件能够实现自由灵活的配置和定制开发所以它是现在热度最高的开源平台。大量极客用户组成了庞大而活跃的社群他们贡献了很多基于Home Assistant的开源组件和丰富的使用资料。
这些组件要怎么配置呢在Home Assistant中你需要通过configuration.yaml配置文件来定义你需要使用的组件以及组件之间的联动方式等。
从文件的扩展名你应该可以看出,它的配置文件使用的是[YAML语言](https://yaml.org/)。这种语言是专门为配置文件设计的写起来比JSON要简单得多。
另外Home Assistant是基于Python 3语言实现的理论上支持在任何有Python 3的系统环境中运行当然也包括树莓派。它对于树莓派的支持非常好。
虽然是开源软件但是我们也需要注意开源许可协议。Home Assistant的代码采用的是[Apache License 2.0](https://github.com/home-assistant/core/blob/master/LICENSE.md)协议。
<li>
<h4>Domoticz</h4>
</li>
第2个要讲的是[Domoticz](https://www.domoticz.com/)它是一个开源的家居自动化平台可以帮你实现对智能家居设备和传感器的控制和监测等目的。除了它原生支持的设备你还可以使用很多开源插件。当然你也可以选择用Python语言开发自己的插件从而支持自己特有的设备。
Domoticz主要是通过Web界面来进行自定义和配置。它还同时提供了iOS和Android两大系统的App供你查看和控制设备。
Domoticz是基于C++语言开发的。跟Python语言开发的软件不同它还需要编译不同的安装包这显然不够方便。不过Domoticz官方已经为Windows和Linux系统准备了二进制安装包包括使用ARM架构芯片的树莓派也已经有了编译好的可执行文件。
Domoticz代码采用的是[GNU General Public License v3.0](https://github.com/domoticz/domoticz/blob/master/License.txt)GPLv3开源协议。
<li>
<h4>openHAB</h4>
</li>
第3个要讲的是openHAB。它的出发点是提供一个家庭自动化的总线所以在设计上尽量抽象而模糊设备本身便于你根据需求添加设备和插件。在openHAB中插件是通过Bindings来实现设备和openHAB内核的交换信息的。
交互界面方面openHAB也提供了丰富的选择包括原生的iOS和Android平台的App以及Web页面和Windows 10应用程序等。
openHAB是基于Java语言开发的。除了常见的电脑平台它还支持用Docker镜像的方式安装使用。另外它还专门提供了openHABian系统的镜像文件所以在树莓派上使用openHAB软件是非常方便的。
openHAB代码采用的是[Eclipse Public License 2.0](https://github.com/openhab/openhab-core/blob/master/LICENSE)开源协议。
<li>
<h4>Gladys Assistant</h4>
</li>
第4个要讲的是[Gladys Assistant](https://gladysassistant.com/en/)。它是一个刚刚重新设计和开发的智能家居平台。它原生支持Z-Wave、MQTT和小米等设备接口并且在平台中嵌入了自己的 NLP自然语言处理对话引擎。你可以在平台上定义复杂的场景模式。
Gladys Assistant的主要交互界面是Web页面。不过这些Web界面在手机上的体验也非常好因为它是基于PWAProgressive Web App实现的。PWA技术使Web应用在手机的体验基本和原生App一致。
Gladys Assistant使用JavaScript语言实现的。它对于树莓派的支持非常不错提供了基于Raspbian的系统镜像。另外通过Docker的方式对MacOS和Windows系统也提供了支持。
Gladys Assistant代码采用的是 [Apache License 2.0](https://github.com/GladysAssistant/Gladys/blob/master/LICENSE)开源协议。
<li>
<h4>WebThingsIO</h4>
</li>
不知道你是否还记得,我在加餐的书单推荐中提到过[WoT](https://www.w3.org/WoT/)标准。这里我要介绍的[WebThingsIO](https://iot.mozilla.org/)项目正是Mozilla在领导开发的一个基于WoT理念的开源实现。
其中WebThings Gateway是一个智能家居网关软件也就是控制中心实现你可以使用它提供的Web界面来实现对设备的查看和控制。它也支持定义联动的规则还提供了用插件方式来扩展对其他设备的支持。
WebThings Gateway主要是使用JavaScript语言实现的。它为树莓派专门制作了系统镜像文件你可以直接烧录使用。对于常用的Linux系统也可以直接通过软件包管理器安装使用。
WebThings Framework是一个软件组件集合你可以基于这些组件来开发支持[Web Thing API](https://iot.mozilla.org/wot/)的硬件设备。它有不同的编程语言实现包括Node.js、Python包括MicroPython、Java、Rust、C++Arduino实现
WebThingsIO的代码采用的是[Mozilla Public License 2.0](https://github.com/WebThingsIO/gateway/blob/master/LICENSE)开源协议。
这里我把这5个开源的智能家居平台的特性总结为一个表格方便你随时参考。
<img src="https://static001.geekbang.org/resource/image/3f/6a/3f957676a982afacb7619925ed19986a.jpg" alt="">
## Gladys Assistant具体使用动手实践
这5个比较有代表性的智能家居开源平台对于树莓派的支持都不错的。现在我们就开始动手在树莓派上安装一个开源平台体验一下通过智能家居系统监测设备数值的过程。
使用哪个平台呢我们就选择Gladys Assistant吧。它的优势是Web页面在手机上体验不错不需要你去搜索或者编译手机上的App了。
其他几个开源平台的安装和使用方法都差不多。你掌握了一个,那么利用镜像文件安装其他几个也不成问题。
### 第一步:准备器材
首先我介绍一下需要准备的器材有哪些:
1. **树莓派Raspberry Pi 4B**要求内存2GB以上但是8GB内存版本要谨慎选择因为有些开源平台软件对Arm 64bit芯片支持不够好。
1. **供电电源**要求支持3A以上电流。
1. **Micro SD卡**也叫TF卡存储容量最好在16GB以上。在选择的时候你要关注读写速度等级比如U3表示最低写入速度是30MB/s。同时你也要关注[应用性能等级](https://www.sdcard.org/chs/developers/overview/application/index.html)它定义了Micro SD卡进行随机读写的性能最好是选择Application Performance Class 2卡面上标识A2图标。在卡上存储应用程序和应用程序数据时这个性能指标非常重要。
<img src="https://static001.geekbang.org/resource/image/f4/66/f4928d8a87bde8b4e235b8cdf057d266.jpg" alt="">
1. **Micro SD卡读卡器**。有些电脑自带这个接口,如果没有的话,你可以买一个便宜的使用。
1. **普通网线**。如果你希望以有线的方式使用树莓派可以准备一根。同时我也会介绍树莓派接入Wi-Fi的方式。
### 第二步:烧录系统镜像
树莓派板子在启动的时候会从SD卡读取操作系统镜像文件完成操作系统的引导启动工作。所以我们接下来要在SD卡上烧录系统镜像。
具体怎么烧录呢?我们可以使用一个免费的烧录工具,**Etcher**。它支持MacOS、Windows和Linux三种主流的电脑系统你可以从[官方网站](https://etcher.io/)上下载和安装。也可以点击[这个链接](https://github.com/balena-io/etcher/releases)下载最新版。
然后,下载树莓派的系统镜像文件。树莓派有官方的操作系统镜像 Raspbian可以选择但是为了避免手动在 Raspbian系统中安装 Gladys Assistant 软件的麻烦我们直接选择官方提供的已经配置好Gladys Assistant的Raspbian镜像文件。
从[这个链接](https://cdn.elephantcdn.com/gh/gladysassistant/gladys/releases/download/v4.0.0/gladys-4.0.0-rev3.img.zip)中下载好镜像文件,并且解压缩得到"img"扩展名的文件。然后把Micro SD卡插入读卡器或者直接插入你的电脑接口中。运行Etcher软件按照步骤把镜像文件烧录到存储卡中。
<img src="https://static001.geekbang.org/resource/image/1b/73/1bcbb300760c8febe63898a0174c3273.png" alt="">
树莓派支持网线接口不过如果你希望树莓派接入家里的Wi-Fi热点而不是使用网线访问网络那么就需要在Micro SD卡中增加一个配置文件。
这个配置文件的文件名必须是wpa_supplicant.conf。你可以在电脑上使用自己熟悉的文本编辑器创建这个文件并且在文件中输入下面的内容
```
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=CN
network={
ssid=&quot;你的Wi-Fi热点SSID名称&quot;
psk=&quot;你的Wi-Fi热点密码&quot;
key_mgmt=WPA-PSK
}
```
注意将Wi-Fi热点的SSID和密码替换为你家里的Wi-Fi路由器的真实情况。
然后将这个文件拷贝到Micro SD卡的根目录。这时你就可以把Micro SD卡插入树莓派开发板了。
### 第三步:启动系统
烧录好镜像文件准备好Micro SD卡后你可以把Micro SD卡从读卡器取出插入树莓派的对应接口中。
接着,接上电源线(如果你使用网线方式,记得也将网线接入树莓派板的网口上)。这时树莓派将自动启动运行,需要等待一段时间。
过一会儿之后,在你的电脑上,打开浏览器,输入 "[http://gladys.local](http://gladys.local)" 来访问树莓派上的Gladys Assistant系统如下图所示。
如果你使用的电脑系统是比较老的Windows版本可能无法通过这种方式访问树莓派而是需要将“gladys.local”替换为树莓派的IP地址。
怎么获取到树莓派的IP地址呢你可以使用一些网络分析软件比如Android手机上安装Network ScanneriOS手机上安装iNet来扫描网络。
<img src="https://static001.geekbang.org/resource/image/60/cf/600490ac2f908cb13ce81b379a8fdacf.png" alt="">
那么为什么不给树莓派接一个显示器而要通过你的电脑上的浏览器来访问呢这是因为Gladys Assistant 为了简化镜像,没有提供可视化的桌面环境。其实你只要熟悉了操作方法,就会发现这样的设计是非常方便的,因为你不需要为树莓派再准备个显示器了。
### 第四步在Gladys Assistant上添加设备
因为我们是第一次配置Gladys Assistant所以要在上面的页面中选择“Create local account with your Email”来创建一个本地账号。
创建完账号后其他的步骤不是必填项你根据喜好填写就行了。房屋配置部分你可以创建一个名称为bedroom的房间后面会用到。最后你应该可以看到这个Dashboard页面。
<img src="https://static001.geekbang.org/resource/image/14/3a/14de681771577c2b8d56fc3590f12d3a.png" alt="">
接下来我们来添加一个设备来体验一下Gladys Assistant的功能。
那么添加什么设备呢我们还没有讲到实战篇的具体项目呢。不过我们可以用电脑来模拟一个设备。你还记得第8讲介绍的MQTT协议吗Gladys Assistant系统支持MQTT通信协议的设备。所以我们可以通过电脑终端的命令行来模拟一个设备发送MQTT消息给Gladys Assistant让Gladys Assistant来展示这个设备。
首先我们在Gladys Assistant上准备MQTT Broker。选择Dashboard 上部的标签页“Integrations”点击MQTT标签。
<img src="https://static001.geekbang.org/resource/image/a2/ab/a2f23d7ce6f2e95d557dce9c93481dab.png" alt="">
进入MQTT设置界面后你现在需要点击左边的Setup开始安装MQTT Broker。我们通过默认选项即使用Docker的方式来完成。
<img src="https://static001.geekbang.org/resource/image/ce/1a/ce2016b8d4ee5ed95e91856a04f44d1a.png" alt="">
这个过程需要几分钟时间。完成后你就可以看到MQTT Broker的用户名和密码。我将密码改成了geektimetest为了便于记忆你也可以选择一个自己的密码。同时你需要将用户名和密码记录下来因为后面模拟设备发送MQTT消息时会用到。
<img src="https://static001.geekbang.org/resource/image/52/89/52d0ccc87753ea79c670d8d5b798dd89.png" alt="">
接着我们点击左边的Device切换到MQTT设备页面。点击右上角的New打开创建页面。设备名称我们可以选择Temperature SensorExternal ID填写mqtt:bedroom:temperature-sensor。字符串中bedroom是和下一项房间填写的信息一致的。
<img src="https://static001.geekbang.org/resource/image/55/d6/554e038f3a7a7f8e2bbd3c14632269d6.png" alt="">
然后我们为设备添加特性Features。这里我们选择Temperature 类型。
<img src="https://static001.geekbang.org/resource/image/e8/db/e8f806569bf36852abe3712c6fed5fdb.png" alt="">
选择完类型后我们点击Add feature开始设置具体的特性参数。你可以按照下图的内容来设置自己的参数。其中external ID填写mqtt:bedroom:temperature-sensor:temperature字符串。最下面的MQTT Topic你需要记录下来后面发送消息时需要用到。
<img src="https://static001.geekbang.org/resource/image/a0/ae/a03735551ee3e3124131169c1b4d60ae.png" alt="">
点击下部的保存Save和返回Back然后我们可以看到创建完成的MQTT设备。
<img src="https://static001.geekbang.org/resource/image/cc/e2/cc37b26e8d548465a4c3aa1dba87a1e2.png" alt="">
在模拟发送MQTT消息之前我们还需要编辑一下Dashboard界面。你需要点击左上角切换回Home标签页然后点击右上角的Edit按键。在编辑界面的Column 1 选择Devices in room类别。然后依次选择bedroom和我们刚创建的设备Temperature Sensor传感器。
<img src="https://static001.geekbang.org/resource/image/2e/9e/2e2bd7fyyaa7ba7b7eb79026db90f89e.png" alt="">
点击页面右下角的Save保存Dashboard的编辑界面。
<img src="https://static001.geekbang.org/resource/image/16/07/16ca1f18eb80eb0fd2efd37d04a49c07.png" alt="">
当然,现在传感器没有数值显示。
### 第五步模拟MQTT设备
接下来,我们打开一个终端命令窗口,输入下面的命令:
```
hbmqtt_pub -d --url mqtt://gladys:geektimetest@gladys.local:1883 -t gladys/master/device/mqtt:bedroom:temperature-sensor/feature/mqtt:bedroom:temperature-sensor:temperature/state -m 25.2
```
其中gladys:geektimetest是用户名和密码它们以分号相连。gladys.local是树莓派的域名。-t后面的消息主题就是你刚才记录的MQTT Topic字符串。-m后面是温度数值。
执行这个命令后你再打开Gladys Assistant的Dashboard界面这时你就可以看到设备卡片显示出来了刚才发送的温度数值。
<img src="https://static001.geekbang.org/resource/image/c9/4d/c931174c7c4dd9cafde673ee795bf24d.png" alt="">
## 小结
总结一下在这一讲中我介绍了智能家居开源平台的生态并且带你动手在树莓派上安装了Gladys Assistant然后体验了一下使用过程。
1. 智能家居平台为各种智能家居产品提供了一个统一的接入系统,让用户可以管理和控制产品,同时也为各种智能家居产品之间的联动提供了条件。
1. 了解开源的智能家居平台有两大好处,一是你可以基于它们快速地搭建智能家居系统,另一个是你可以在工作中借鉴它们的设计思想和架构。
1. 开源的智能家居平台有很多热度比较高的平台有5个分别是Home Assistant、Domoticz、openHAB、Gladys Assistant 和WebThingsIO。
1. 利用开源平台,在树莓派开发板上搭建智能家居系统的一般步骤包括:准备器材,烧录系统镜像,启动系统,添加设备,接收和显示设备信息等。
在实战篇,等你掌握了开发智能硬件的基本方法,可以将这一讲中的模拟设备替换为真实的温度传感器设备。期待你的动手实践。
而且之后,等你熟悉树莓派开发板的使用方法,你也可以在树莓派上尝试一下其它几个开源平台。它们的基本功能都比较接近,你可以根据自己喜好,或者熟悉的编程语言选择。
## 思考题
最后,我给你留一个思考题吧。
在这一讲的动手实验环节,我们是通过访问[http://gladys.local](http://gladys.local) 来查看Gladys Assistant的Dashboard等Web应用界面的。为什么我们可以通过gladys.local 域名来访问树莓派开发板呢?为什么有的老版本电脑系统不支持呢?这是使用了什么网络协议?
欢迎你在留言区写一写自己的答案,也欢迎你将这一讲分享给对智能家居 DIY感兴趣的朋友大家一起交流学习。