1. 程式人生 > >JavaScript —— 下一代物聯網全棧開發

JavaScript —— 下一代物聯網全棧開發

作者簡介:李知周,中國科學院微系統與資訊科技研究所博士,物聯網早期創業者,發起了開源物聯網專案 Openfpgaduino,目前在國際知名投資銀行從事基於大資料與機器學習的網路安全開發。
本文為《程式設計師》雜誌原創文章,未經授權,請勿轉載
關注公眾號“CSDN 物聯網開發”微信公眾號,瞭解更多物聯網資訊與乾貨

Jeff Atwood 曾提出“任何能夠用 JavaScript 實現的應用,最終都必將用 JavaScript 實現”他對 JavaScript 的推崇不言而喻。而當下這個物聯網大熱的時代,JavaScript 支援 HTTP 和 JSON、支援函數語言程式設計、可提供互動式環境等特點堪稱適用於物聯網全棧開發。本文詳解了 JavaScript 可參與的每一個物聯網過程,並針對開源專案進行了彙總。

物聯網節點 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 的開發應用已經有很長一段時間了,可以從伺服器開發裡借鑑不少工具。

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 解釋引擎採用

Esprunio(https://github.com/espruino/Espruino)是為微控制開發的嵌入式 JavaScript 解釋引擎,提供了最小化的配置,可以在記憶體低至 8KB 的微控制器上執行 JavaScript 語言。

Cyclon(https://github.com/hybridgroup/cylon)是使用 JavaScript 來做機器人控制的專案,支援市面上主要的開發板,通過提供各種外設的驅動,實現了不需要寫一句 C 語言就能開發和組裝一個機器人。

物聯網 JavaScript 大資料處理

現在大資料技術方興未艾,在這些技術中,最基本的思想是 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 在資料視覺化方面的成功,幫助了它能夠很方便地視覺化機器學習的過程與結果,方便以遠端方式來控制機器學習叢集。

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 不僅可以給使用者提供互動式的環境,而且可以做出非常漂亮的圖表與動畫效果,包括兩維的圖表曲線以及三維的虛擬空間,都可以使用 JavaScript 控制瀏覽器來實現,現在許多基於瀏覽器的 VR 環境也可以通過 JavaScript 來開發了。使用 JavaScript 來做物聯網的資料展示實在太合適不過了。

當今網際網路世界,JavaScript 已經是一種全棧網路開發語言。特別是 HTML5 興起以後,JavaScript 不再侷限於瀏覽器端,而是將觸手伸到了網路的方方面面,包括物聯網端、手機的移動端和傳統的 PC 端。HTML5 為瀏覽器設計,很好地發揮了 JavaScript 的跨平臺特點,真正做到了開發一次 JavaScript 程式碼,從手機上的 iOS、Android 到電腦端的 Windows、macOS、Linux 的跨平臺執行。雖然目前 JavaScript 在效能上仍然無法與原生開發應用相匹敵,但是僅使用單一的 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 例項講解

為更好理解 JavaScript 物聯網開發,下面以筆者設計的物聯網系統為例,詳細介紹物聯網系統的設計。所有設計資料都是開源的,讀者可以對照原始碼網頁與文件。( http://openfpgaduino.github.io

物聯網閘道器最小系統硬體設計

對於應用於物聯網的嵌入式系統,系統的硬體設計是重要的組成部分,通常來說應對不同的應用,嵌入式硬體的系統設計會有所不同,每個系統都會按照相應的應用場景進行調整。但是物聯網的核心繫統通常是一個設計構架可以應用於一大類的系統,所需要調整的通常是外圍裝置。


圖 1

如圖 1 所示是筆者設計的物聯網閘道器最小系統,在系統設計思想上首先保證了核心系統的通用性,採用了 ARM 處理器。因為是閘道器節點,設計上需要閘道器直接連線 IP 網路並提供全功能的網路服務,因此選擇了能夠完整執行 Linux 系統的 ARM9 處理器,執行完整的 Linux 也是能夠執行 Node.js 與完整 JavaScript 的必要條件。由於採用了標準 Linux,雖然 Linux 核心提供了一些實時性補丁,但由於程式設計模型複雜而且也不適合與 Node.js 整合,因此在最小核心系統中加入了 FPGA。FPGA 能夠在硬體層面完成實時響應,解決了系統的實時響應需求,其次由於 FPGA 是一種可配置系統,能夠根據應用需要進行設計與配置,所以引入 FPGA 也極大提高了系統的適配能力,為靈活適配各種不同物聯網應用提供了基礎。

物聯網網關係統軟體設計

物聯網閘道器的軟體設計完全是基於硬體設計的構架,最大限度發揮硬體的功能,設計流程如下:

  1. 首先在 ARM9 處理器上執行完整的 Linux 系統,其擁有完整的網路服務,並能根據需要通過 APT-GET 命令來安裝所需要的軟體包。
  2. 在 Linux 系統基礎上,執行 Node.js+MongoDB 來提供所有網路服務。
  3. 在 Node.js 裡執行 HTTP 伺服器來提供基於 Restful API 的各種本地服務,包括各種外設與 I/O 的控制。
  4. 執行 HTTP 伺服器,基於 RESTful API,實現了基於 HTML5 的雲 IDE 開發環境。方便技術極客對閘道器的功能進行定製,這些 IDE 包括了 JavaScript 的 IDE,基於圖形程式設計的 Blockly IDE 和可以配置 FPGA 模組功能和引腳定義的 Web 設計工具,當然也包括較為底層的 C 語言 IDE。

有了這些功能,使用者就可以根據自己的需要,編寫簡單的程式來控制與物聯網閘道器連線的任何裝置,實現自己的智慧物聯網。
在板子內部,通過實現 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 中做資料線上分析、機器學習等任務,實現資料在線上分析與響應。

大資料處理系統的設計結構圖

參考文獻