作者简介:李知周,中国科学院微系统与信息技术研究所博士,物联网早期创业者,发起了开源物联网项目 Openfpgaduino,目前在国际知名投资银行从事基于大数据与机器学习的网络安全开发。
本文为《程序员》杂志原创文章,未经授权,请勿转载
关注公众号“CSDN 物联网开发”微信公众号,了解更多物联网资讯与干货
Jeff Atwood 曾提出“任何能够用 JavaScript 实现的应用,最终都必将用 JavaScript 实现”他对 JavaScript 的推崇不言而喻。而当下这个物联网大热的时代,JavaScript 支持 HTTP 和 JSON、支持函数式编程、可提供交互式环境等特点堪称适用于物联网全栈开发。本文详解了 JavaScript 可参与的每一个物联网过程,并针对开源项目进行了汇总。
网络中,HTTP 协议与 JSON 数据格式特别是 RESTful API 无疑具有支配地位,各种云服务,数据传输都基于这些协议来进行。而 JavaScript 为 HTTP 和 JSON 提供了最好的支持,当物联网系统采用 JavaScript 开发时,天然对接了互联网上海量的云服务与云资源,包括云存储、云计算等一系列资源都可被方便调用,就像你在手机端访问各种云服务一样。微服务构架在服务器端的兴起,让 JavaScript 编写的每一个物联网节点都可以作为一个大系统中的微服务,通过 RESTful API 接口提供自己的服务。
在设计模式上,JavaScript 的回调与事件循环等基于事件驱动的编程模型非常适合物联网。在物联网环境下,环境在不断变化,物联网节点要不断对环境的变动做出响应,换句话说物联网系统通常是 I/O 密集型的系统,回调与事件循环高效地完成了密集 I/O 操作这项工作,而事件响应式编程相比于多进程和多线程编程在内存的使用上又非常高效,而这又是物联网系统所需要的,通常物联网系统都是资源受限系统,内存与 CPU 的频率都非常有限。物联网节点底层开发中通常采用中断响应模式,在 CPU 中由称为中断控制器的硬件来检查中断信号的出现,并在中断出现后控制 CPU 执行特定程序片段,这一执行模式和 JavaScript 的回调一致,很容易使用 JavaScript 回调机制来实现硬件的中断响应。
物联网节点的部署特点决定了其回收维护成本非常高昂甚至是不可接受的。而物联网节点要不断应对新的环境与应用需求,所以在开发中物联网节点的远程部署与更新是非常重要的一个功能。JavaScript 本来就是实现从服务器端向客户端部署的一门语言,其天然就具有在网路上实现远程部署的属性,实现起来就像你用浏览器下载 JavaScript 脚本并运行一样简单。JavaScript 的热部署也是一个比较热门的研究领域,通过热部署,物联网节点可以实现在运行过程中远程添加新功能,远程修正 bug。
前面讲了使用 JavaScript 来做物联网开发的好处,那么也有必要讲一下 JavaScript 在物联网应用中相对于 Web 应用的不同以及误区。第一个问题就是实时性。作为嵌入式系统的物联网网关,首要面对的就是实时性问题,比如各种电机的控制,一些电压信号的采集都是有实时性要求的,需要在规定的时间内完成所需工作。由于 JavaScript 的内存管理使用垃圾收集机制,所以必然存在由于垃圾收集而导致应用中断执行的情况,甚至有可能在一些情况下造成实时性要求遭到破坏。所以在设计 JavaScript 物联网实时程序时需要考虑内存垃圾数据时间对实时性的影响。当然,也可以采用另一个设计模式,就是把实时任务使用 C 语言编写的单独线程来实现,利用 JavaScript 的单线程特点,这种设计模式不会在嵌入式系统中占用过多的资源,毕竟 JavaScript 本来就是一种嵌入在浏览器里的语言。另一方面,由于物联网网关是需要长期运行的设备,相对于 Web 端需要有更高的可靠性与稳定性,所以单元测试与集成测试是必须的,而且需要使用代码分析工具保证代码没有任何内存及文件句柄的泄漏。即使在垃圾收集环境下,内存泄漏也是存在的,而在嵌入式系统中由于内存有限,特别容易泄漏。好在服务器端 JavaScript 的开发应用已经有很长一段时间了,可以从服务器开发里借鉴不少工具。
OpenFPGAduino(https://github.com/OpenFPGAduino/)是笔者设计的基于 Node.js 与 FPGA 的开源物联网软硬件开发平台,与 Arduino 等快速原型开发平台类似。基于 Node.js 提供了网络交互能力,特别包括用于开发的网络 IDE 以及类似于树莓派的 Linux 软件开发系统,方便安装各种 Linux 通用软件,并且通过 FPGA 提供了可编程的兼容 Arduino 外围接口用于与丰富的物联网传感器进行连接。
iotjs(https://github.com/Samsung/iotjs)是三星开发的嵌入式 JavaScript 执行环境,能够在几百 KB 内存的嵌入式系统上,不依靠操作系统实现完整的网络与 JavaScript 支持。JavaScript 解释引擎采用
JerryScript(https://github.com/jerryscript-project/jerryscript),事件循环采用 libtuv。可以说是 Node.js 的嵌入式版本,其 API 与 Node.js 相兼容。
Esprunio(https://github.com/espruino/Espruino)是为微控制开发的嵌入式 JavaScript 解释引擎,提供了最小化的配置,可以在内存低至 8KB 的微控制器上执行 JavaScript 语言。
Cyclon(https://github.com/hybridgroup/cylon)是使用 JavaScript 来做机器人控制的项目,支持市面上主要的开发板,通过提供各种外设的驱动,实现了不需要写一句 C 语言就能开发和组装一个机器人。
现在大数据技术方兴未艾,在这些技术中,最基本的思想是 MapReduce,这一思想将不同运算拆解为 Map 与 Reduce,然后将这些 Map 与 Reduce 任务在集群当中最大限度地并行执行。实现 MapReduce 模式编程最重要的一点就是支持函数式编程或者又称为 Lambda 编程,所有的 Map 与 Reduce 操作都接受函数式编程作为参数。大数据的成功推动了函数式编程的复兴,由此多数现代编程语言包括 C++与 Java 的新版本均支持函数式编程。JavaScript 在它出生的那一天就支持函数式编程,其回调函数就是一种函数式编程模式。自然而然,支持 Map 与 Reduce 也不是什么困难的事情,因此使用 JavaScript 做物联网大数据分析与处理非常容易而高效。JSON 数据格式因为格式简单、处理方便的特点而受到不少青睐,被大量应用于 HTTP 网络传输,现有互联网上的基础设施与云服务也都采用 JSON 格式。以这些设施作为数据源,就要使用 JavaScript 来处理大数据,这是因为 JSON 作为 JavaScript 代码片段,本质上来说一切基于 RESTful API 设计的接口几乎都是对 JavaScript 友好的,当然为了效率,有时会使用 JSON 的二进制形式 BSON。
由于物联网的对称性,物理网的节点不仅可以是数据的收集者,同样可以是数据的发起者,使用 JavaScript 来处理物联网大数据,那么每个物联网数据节点也能够发起大数据的处理,并利用后台大数据环境做出决策并响应环境的变化。这样利用 JavaScript 将物联网前端和后端数据处理直接无缝链接起来,实现了大数据的实时处理与响应的 Lambda 构架,并同时完成了批处理与实时处理构架的统一。在未来,可以用 JavaScript 设计基于 Node.js 的处理框架,把物理网节点当作大数据节点来统一物联网中数据的处理与传输。
机器学习作为现在最热门的大数据处理手段不得不提。由于 JavaScript 在浏览器端的支配地位,使用 JavaScript 做机器学习的尝试也非常多,有了 JavaScript 的机器学习库,就可以非常简单地在浏览器上运行一些机器学习的小程序,同样也可以将这些机器学习库运行在 Node.js 上,使得物联网节点也具有一定的机器学习能力及数据处理能力。当然使用 JavaScript 直接控制深度机器学习集群也是一个不错的选择,JavaScript 在数据可视化方面的成功,帮助了它能够很方便地可视化机器学习的过程与结果,方便以远程方式来控制机器学习集群。
Eclairjs(https://github.com/EclairJS/eclairjs-node)是基于 Node.js 的 Spark 大数据处理平台前端,通过 Eclairjs,可以使用 JavaScript 来操作基于内存处理的大数据分析平台 Spark,通过 Spark 内置的调度器,实现了在整个集群上调度资源,以最大化并行度来执行 JavaScript 的算法。目前,Spark 是最主流和发展最快的大数据处理平台。
skale(https://github.com/skale-me/skale-engine)是一个完全使用 JavaScript 开发,采用 Node.js 作为执行环境的分布式数据处理系统,具有比 Spark 更快的性能。
MQTT 是为物联网设计的轻量级通讯协议,协议基于 TCP/IP,适用于低带宽不可靠环境。协议的开销非常小,支持发布订阅模式,是种非常高效的通信协议。MQTT.js(https://github.com/mqttjs/MQTT.js)是 Node.js 的 MQTT 通信模块,实现了 Node.js 收发 MQTT 数据。
Kafka 是大数据分布式消息总线,提供了对海量消息的持久化能力,通过将接收到的数据直接持久化到硬盘,提供了稳定的数据吞吐量以及高可用性。Kafka Rest(https://github.com/confluentinc/kafka-rest)模块为 Kafka 消息总线提供了 Restful API 的支持,无论是使用浏览器还是 Node.js 都可以方便使用 JavaScript 来收发消息。
convnetjs(https://github.com/karpathy/convnetjs)是完全用 JavaScript 写成的神经网络机器学习库,可以运行在浏览器中或 Node.js 中。convnetjs 提供了许多有趣的例子,在浏览器上实现了对神经网络学习过程的可视动画呈现与交互模式,对理解神经网络机器学习有非常好的帮助。
作为深度神经网络学习方向最重要也是最热门的项目,Google 的 TensorFlow 是深度学习开源的里程碑。TensorFlow 的开源使得使用深度学习来做数据处理已经不再是一件高难度的事情。Node-tensorflow(https://github.com/node-tensorflow/node-tensorflow)是一个 NodeJS API,使用了 Google 的开源机器学习库 TensorFlow。
在浏览器端,JavaScript 在数据的可视化方面早已处于支配地位,作为现在几乎唯一可在所有浏览器上运行的编程语言,使用 JavaScript 不仅可以给用户提供交互式的环境,而且可以做出非常漂亮的图表与动画效果,包括两维的图表曲线以及三维的虚拟空间,都可以使用 JavaScript 控制浏览器来实现,现在许多基于浏览器的 VR 环境也可以通过 JavaScript 来开发了。使用 JavaScript 来做物联网的数据展示实在太合适不过了。
当今互联网世界,JavaScript 已经是一种全栈网络开发语言。特别是 HTML5 兴起以后,JavaScript 不再局限于浏览器端,而是将触手伸到了网络的方方面面,包括物联网端、手机的移动端和传统的 PC 端。HTML5 为浏览器设计,很好地发挥了 JavaScript 的跨平台特点,真正做到了开发一次 JavaScript 代码,从手机上的 iOS、Android 到电脑端的 Windows、macOS、Linux 的跨平台运行。虽然目前 JavaScript 在性能上仍然无法与原生开发应用相匹敌,但是仅使用单一的 JavaScript 来开发各种应用程序,开发成本与响应速度上是其他开发方式所无法比拟的。特别是在物联网开发环境中,很多应用是轻量级的,对性能没有特别高的要求,但是却需要依照不同物联网环境进行不同的定制与适配,JavaScript 来做这样的数据展示工作再合适不过。根据网络的对称性特点,甚至于,完全可以用 JavaScript 来实现在物联网节点上完成数据展示的工作,一如我们用手机来进行数据展示与控制。
D3(https://github.com/d3/d3)是基于 SVG 的矢量图操作工具。通过使用 JavaScript,D3 将数据以矢量图形式展示出来并提供了任意伸缩的能力,在 HTML5 出现之前,D3 是二维可视化的底层标准。通常许多绘制图表与数据可视化工具都使用 D3 作为底层驱动引擎,在其之上提供用户友好的 API 接口。
three.js(https://github.com/mrdoob/three.js/)是基于 WebGL 的 JavaScript3D 绘图工具。通过 WebGL 扩展,JavaScript 语言实现了直接驱动底层显卡进行 3D 绘图。three.js 在 3D 图像基础上还提供了 VR 支持,可以方便的使用 JavaScript 来实现 VR 数据展示。
ionic(https://github.com/driftyco/ionic)是基于 AngularJS 的移动端跨平台开发环境,能够在一个统一的框架下使用 JavaScript HTML CSS,同时完成开发 iOS、Android、Window Phone 程序,并根据各个环境的不同 UI 做自动适配。
Electron(https://github.com/electron/electron)是基于 Node.js 与 chrome 的跨平台桌面开发环境,electron 通过将浏览器内核和后台 Node.js 服务进行打包,实现了一个使用 JavaScript 的全栈开发环境,无需任何修改就能够将程序部署到 Windows、macOS、Linux 平台上。
nw(https://github.com/nwjs/nw.js)是轻量级的 JavaScript 跨平台桌面开发环境,它实现了在 HTML 代码中直接调用 Node.js 的各种 API 函数,提供了一个平滑自然的桌面应用开发环境,隐藏了客户端服务器编程的细节。
为更好理解 JavaScript 物联网开发,下面以笔者设计的物联网系统为例,详细介绍物联网系统的设计。所有设计资料都是开源的,读者可以对照源码网页与文档。( http://openfpgaduino.github.io)
对于应用于物联网的嵌入式系统,系统的硬件设计是重要的组成部分,通常来说应对不同的应用,嵌入式硬件的系统设计会有所不同,每个系统都会按照相应的应用场景进行调整。但是物联网的核心系统通常是一个设计构架可以应用于一大类的系统,所需要调整的通常是外围设备。
如图 1 所示是笔者设计的物联网网关最小系统,在系统设计思想上首先保证了核心系统的通用性,采用了 ARM 处理器。因为是网关节点,设计上需要网关直接连接 IP 网络并提供全功能的网络服务,因此选择了能够完整运行 Linux 系统的 ARM9 处理器,运行完整的 Linux 也是能够运行 Node.js 与完整 JavaScript 的必要条件。由于采用了标准 Linux,虽然 Linux 内核提供了一些实时性补丁,但由于编程模型复杂而且也不适合与 Node.js 集成,因此在最小核心系统中加入了 FPGA。FPGA 能够在硬件层面完成实时响应,解决了系统的实时响应需求,其次由于 FPGA 是一种可配置系统,能够根据应用需要进行设计与配置,所以引入 FPGA 也极大提高了系统的适配能力,为灵活适配各种不同物联网应用提供了基础。
物联网网关的软件设计完全是基于硬件设计的构架,最大限度发挥硬件的功能,设计流程如下:
有了这些功能,用户就可以根据自己的需要,编写简单的程序来控制与物联网网关连接的任何设备,实现自己的智能物联网。
在板子内部,通过实现 Linux 内核驱动的用户空间模式,实现了 Node.js 直接访问 FPGA 内存空间、控制 FPGA 内部逻辑,并最终控制与 FPGA 相连接的各种物联网外设的功能。同时还实现了 Node.js 对 FPGA 的现场再配置,这样直接通过网络下载 FPGA 配置文件,即可现场改变整个核心系统。
如图 3 所示是物联网网关的云构建系统,图中可以看到物联网云构建系统由 GitHub,Docker Build 与七牛云存储三部分组成。云构建系统的想法来源于持续集成,在软件工程里,持续集成的概念已经非常普遍了,每次有代码改动的提交都会触发一个编译测试的持续过程。模仿软件持续集成,在云端构建了一个对应于物联网网关的持续集成环境,选择云端是因为嵌入式系统本身的能力与处理速度都有限,不适于做为持续集成的构建环境。在云构建系统中,使用 GitHub 来管理代码的基础版本,使用 Docker Hub 的容器构建机制完成应用程序与 FPGA 程序的构建,最后使用七牛云做为构建结果的分发平台。所有开发板通过 RESTful API 驱动云编译环境,并访问七牛云来实现程序配置的升级。利用这一套云持续集成系统,不仅可以迅速对应用进行部署而且可以开放支持用户进行自定义开发的云开发环境,用户利用这一云环境,能够轻松实现对 FPGA 的定制设计,实现一套云端 FPGA 设计环境。
如图 4 所示是物联网大数据处理系统的设计构架图,物联网网关上收集到的物联网数据被系统上的 Node.js 源源不断地送往大数据消息总线 Kafka 的 HTTP proxy,每一个 Kafka HTTP proxy 可以承接数千个这样的物联网网关,而 proxy 又将汇总的的数据发送到 Kafka 的集群中做进一步汇总,然后在 Kafka 集群内部实现对数据的 ETL 过程,包括数据异常值的过滤,重复数据的去除,数据格式的转换。最后利用 Kafka 集群,将数据发送到不同的下游消费者那里,先发送到大数据存储系统 S3 或者 HDFS 做永久保存,为将来用 Hadoop 或者 Spark 等离线分析系统提供数据;接着数据被发送到 ElasticSearch 中做索引,方便对数据进行快速的搜索与查询,并且利用 Kibana 做数据的可视化,提供整个数据流的健康状态监控;最后数据被发送到实时在线大数据处理系统 Spark Streaming 中做数据在线分析、机器学习等任务,实现数据在在线分析与响应。
大数据处理系统的设计结构图