【搭建物聯網後臺】基於Workerman的物聯網後端管理平臺設計



寫在前面

最近開發了一款關於物聯網項目的後端管理平臺,可以實現對設備的管理,包括設備的連接、區分,狀態反饋的推送、記錄,對設備的控制等操作(這裏的設備是護理牀,主要可以實現對姿態的實時記錄、控制)。以及,用戶端可以實現多種終端多對一設備綁定,監控。

其管理平臺的業務特徵如下:

  • 設備是基於TCP長連接,指令/反饋不定期實時推送,因此需要長連接雙向通信。
  • 設備需要後端平臺的管理支撐,如設備的區分,數據長久化保存,設備批量化管理
  • 設備需要一對多的綁定,實現多用戶不同終端的實時監控
  • 用戶端需要查詢設備的實時狀態(觀察者權限)、授權控制設備(控制者權限)

這裏把項目(後端,前端)中的一些設計思路和開發心得整理記錄下來。如果你的業務中也需要設備管理模塊的設計開發,或者正好需要上述特徵,下面的介紹或許對你有所幫助。當然有錯誤之處,也請批評指正。

(~ ̄▽ ̄)~


後端的選擇與設計

首先我們思考下物聯網後端的設計難點。如何選擇和設計物理網項目的後端框架呢?

——>難度1:選擇

需要什麼樣的通信方式(協議),決定了使用什麼樣的服務器框架。

與傳統的web服務器不同,我們這裏的設備由於需要基於TCP的長連接、實時通信,在後端中我們需要實現socket通信。而用戶端採用應用層的協議通信,便於終端的開發。爲此,我們需要一款支持長連接的、多協議轉化的服務器框架。

——>難度2:設計

其次,根據業務的特徵:由於設備端的數據需要實時推送給綁定的用戶,用戶也需要給綁定的設備發送控制指令。

整個業務場景類似於IM聊天室的感覺,只不過這裏的角色不是平等的,具體劃分爲設備端和用戶端(給予不同用戶權限)。我們的後端分別需要對設備管理和用戶管理。

  • 設備的管理主要包括:設備的登錄識別,設備的控制,設備可以反饋什麼狀態、反饋到哪,設備狀態的長久化記錄,具體用戶綁定後與設備的交互,等等
  • 用戶端需要分爲用戶的管理具體用戶的操作:也就是不同的用戶對設備有不同的操作權限(觀察模式和控制模式)
  • 用戶的管理:因爲涉及不到設備模塊,這個可以歸納到web開發中,web開發包括了整個app建設,包括需求、數據。
  • 具體用戶的操作包括:用戶對設備的綁定和解綁,用戶對設備狀態的查詢(查詢數據庫、直接發送指令查詢設備),與設備的交互,等等。

——>整體框架

這裏寫圖片描述

項目後臺的架構圖如上所示。採用模塊化的方式,包括兩個大的模塊:

  • 設備管理模塊(後端:wm模塊):這是我們本篇所討論的
  • 用戶管理模塊(web模塊):後續的開發,不影響wm模塊的開發

web模塊在wm模塊(後端)的基礎上,便於後續的擴展。(目前還沒有設計web模塊,用戶不需要註冊和登錄,直接匿名登錄的方式連接到wm模塊後綁定設備。後面業務邏輯章節有具體說明。)

區別:Workerman是一個通用的socket服務器框架,支持長連接,支持各種協議如HTTP、WebSocket以及自定義協議。而Apache/nginx/php-fpm一般來說只用於開發HTTP協議的Web項目。

我們目前首要的任務是關注設備端的管理,設備與用戶的通信交互。也就是物聯網後端的選擇和設計(設備管理,用戶操作的業務)纔是項目開發的重中之重

我們後端選擇的是workman框架。前面開題提到的管理平臺的業務特徵,其實,80%都是建立在其中的GatewayWorker框架之上的(文中用gw框架代替)。


爲什麼選擇workerman?

初期調研的時候開始尋找是一些物聯網的公有云平臺,但是發現大多都是面向企業的(收費滴~~ ⊙︿⊙),不能個性化定製。對於個人開發者來說主要是不開源的,沒有學習的機會。

後來瞭解到了Workerman(傳送門),一款純PHP開發的開源高性能的PHP socket 服務器框架。

其中二次開發的GatewayWorker(傳送門)進程模型具有高併發,長連接,支持單播、多播(一對多模式)、廣播方式推送消息,支持多協議等特徵。

gw框架是爲聊天室設計的IM服務端,並且開發文檔豐富,上手起來也很快(爲了學習後來自己也基於此DIY了一個聊天室DEMO:基於JavaFX的聊天室ChatRoom

具體的workerman介紹就不在這班門弄斧了,官方的介紹和文檔在上面傳送門中。總之,GatewayWorker的業務邏輯也適合物聯網項目,比如上面提到的GatewayWorker特徵都可以用到項目的業務中。


通信協議的思考

工欲善其事,必先利其器。在介紹如何設計後端之前,先傳授下協議大法。

通信協議可以簡單解釋爲雙方“說話”的規則,需要考慮的問題:數據的形式,怎麼發送數據,數據怎麼被對方理解,等等。

——>爲什麼需要通信協議?

由於TCP是基於流的,客戶端發送的請求數據是像水流一樣流入到服務端,服務端探測到有數據到來後應該檢查數據是否是完整的,因爲可能只是一個請求的部分數據到達服務端,甚至可能是多個請求連在一起到達服務端。如何判斷請求是否全部到達或者從多個連在一起的請求中分離請求,就需要規定一套通訊協議。——workerman

簡單來說,我們這裏說的協議可以歸納爲網絡中應用層的協議,是在TCP基礎上,爲解決網絡中數據包粘包問題。當然協議的作用這只是其中一點。我們主要關心的key:

  • 區分指令/反饋的標識
  • 指令/反饋格式定義

一般通信協議都有現成的技術,根據不同的場景有不同的應用,比如我們熟悉的:

  • http(網站)
  • websocket(長連接)
  • mqtt(物聯網)

當然有時候需要自定義協議,比如用於單片機不需要這麼複雜的:

  • workerman中自定義的text協議
  • 我們項目中設備與服務器通信的數據包協議

但是本質上還是在TCP基礎上簡單實現的。

——> 總結:

  • WebSocket vs HTTP :
  1. 主要考慮用戶可以通過多種終端(網頁,移動,pc)通信,http和websocket基本上都可以做到
  2. 由於指令(發送)和反饋(接收)是實時,有狀態的,雙向通信
  3. 但是http是c/s模式,無狀態、無連接;而websocket支持長連接
  4. websocket 勝!
  • 我們項目中用戶與服務器的通信使用websocket通信的(gw框架中已支持
  • 設備端使用自定義的數據包協議,協議簡單支持單片機解析,需要自己編寫(如何在gw框架中編寫自定義協議,請查看官方手冊,項目源碼中也有例子
  • 由於用戶端和設備端擁有各自的協議,會增加後端開發的難度,因爲需要分別對應解析(框架中設置兩個端口分別接收用戶端和設備端數據) 數據的格式在業務邏輯中需要轉化,比如用戶端的指令需要轉發成數據包的形式發送給設備。

——>下面具體介紹用戶端和設備端兩種數據的消息類型。


後端設計之消息類型

  • 設備端與服務器的消息類型
  • 用戶端與服務器的消息類型

如果說通信協議是各種終端與服務器溝通的橋樑,那麼消息類型就是其中行駛的各類車輛。不同的汽車代表不同的數據,不管是終端還是服務器都需要提前知道其含義(這裏就是設計封裝/解析消息的業務邏輯)。

只有將數據(指令/反饋)封裝成消息類型,數據纔有了靈魂。我們需要考慮的key:

  • 第一點:如何封裝成消息?
  • 第二點:有哪些消息(指令和反饋)?

——>第一點:

首先一條消息要根據需要分爲幾個字段,比如:①既然要區分消息類型,就要有類型字段,②消息的來源,③有些消息還要攜帶內容。

其實,封裝消息就是相當於關聯數組的形式。然後將數組轉換成字符串(用戶端)或字節流(設備端)發送。

項目中的用法:

  • 用戶端採用Json的格式封裝消息,是比較流行的方法,各種終端都有相應的開源API,用起來方便
  • 設備端由於解析的難度,採用常見的簡單數據包格式,變長的數據包具體有兩種(其中關鍵的就是如何判斷數據包的起始和結束):
  • 包頭+包類型+包長度+內容+校驗(項目中使用的)
  • 包頭+包類型+內容+校驗+包尾

——>第二點:

有哪些消息類型就是業務邏輯的事情。

再介紹之前首先明確三種***消息的流向***(結合下章的分析效果更佳):

  • 設備——服務器——>用戶:反饋(如設備狀態信息)
  • 用戶——服務器——>設備:指令,包括:
  • 查詢指令:通用的權限(如姿態查詢)
  • 控制指令:有權限 (如控制姿態)
  • 服務器<——>設備/用戶:服務器反饋(如設備的心跳檢測,用戶/設備的登錄和反饋)

消息類型具體參見(結合下章的分析效果更佳):


後端設計之業務邏輯的設計

建議在思考自己的業務邏輯之前,首先整體閱讀下GatewayWorker文檔(傳送門)!
建議在思考自己的業務邏輯之前,首先整體閱讀下GatewayWorker文檔(傳送門)!
建議在思考自己的業務邏輯之前,首先整體閱讀下GatewayWorker文檔(傳送門)!

好了,現在萬事具備,只欠東風。接下來我們可以大幹一場,開發出屬於自己的需求。當然啦,不同的需求需要不同的業務邏輯。

這裏提煉下我們項目中用到的一些業務邏輯和心得體會,避免走彎路,也許對你會有所啓發。

——>0、概況

首先,我們的後端特徵如下所示:

  • 使用Workerman中GatewayWorker框架(類似IM場景)
  • 長連接、有狀態、雙向通信,實現指令和反饋的不定期實時推送
  • 雙協議:TCP+自定義數據包格式(設備端),WebSocket+Json格式(用戶端)
  • 發佈訂閱模式,實現設備一對多用戶。也可以實現點對點應答模式
  • 業務邏輯清晰:設備登錄,用戶綁定,實時反饋(觀察者權限),授權控制(控制者權限)
  • 設備狀態實時記錄到MySQL數據庫中,支持按日期查詢

簡單的流程圖如下所示:

用戶主機服務器主機設備主機連接請求,攜帶PID(設備標識符)記錄PID,檢查唯一性允許連接發送設備狀態信息解析狀態記錄到數據庫用戶在某刻時間段後登錄登錄,攜帶UID(用戶標識符)記錄個人信息登錄成功發送待連接設備的PID,綁定設備如果該設備在線綁定成功查詢設備狀態發送數據庫記錄的設備當前狀態發送指令動作完成,記錄狀態用戶主機服務器主機設備主機用戶、設備連接建立的過程

——>1、設備的識別和用戶綁定設備

首要解決的問題便是設備連接服務器後的識別、管理,然後用戶綁定對應的設備進行交互。

項目中用到的識別號及作用:

  • id:gw框架中提供的連接識別號(20個字節,全局唯一),不管是設備還是用戶連接到gw框架後都有一個id號,用於gw框架中業務邏輯的開發。
  • pid:設備的識別號(自定義的,6個字節),用於後端區分設備,管理設備,用戶通過pid可以綁定到設備,建立通信。
  • uid:用戶的識別號(目前沒有定義),目前用戶端直連到gw獲取id,後期增加用戶管理(web模塊),實現用戶uid的註冊、登錄、授權控制。

具體的邏輯如下:

  • ①設備連接到服務器後首先需要發送自己的pid,保存在session中
  • ②服務器還需要進行pid有效性檢查、重名檢查等
  • ③用戶綁定設備時需要檢查設備是否在線
  • ④用戶綁定成功時將設備pid保存在gw框架的uid中,使用Gateway::bindUid(gw框架uid與用戶uid不同,原理請查看官方手冊)
  • ⑤每次服務器向設備發送消息時也需要檢查設備是否在線
  • ⑥多個用戶和綁定的設備可以發送指令和反饋(具體如何實現交互的,結合下文3.一對多模式介紹)
  • ⑦用戶下線後會自動解綁,或手動解綁Gateway::unbindUid

——>2、觀察者、控制者用戶權限

結合上文的三種消息流向,我們設計了兩種用戶權限:

  • 觀察者權限:設備的反饋信息發送給用戶,和一些查詢設備指令
  • 控制者權限:用戶控制設備,這裏指的是一些受限的控制指令(比如操作設備),並不是所有的指令(比如直接查詢設備)

**一個合法的用戶可以有觀察者權限,但是不一定會有控制者權限。**控制者權限需要授權後獲得。

可以在後期的開發中(web開發)加入用戶角色的管理,目前用戶是直接匿名連接gw框架的。至於控制者權限,需要輸入對應設備的密碼,服務器才接受該用戶對設備的控制。

具體反饋和指令的消息流向是怎麼傳輸的,還需要結合下文的一對多模式來分析。

——>3、設備一對多用戶模式

如果設備和用戶只需一對一的綁定,那麼開發起來很簡單,可以直接在數據庫或session中建立用戶和設備的映射關係,來實現指令和反饋的傳輸。

但是,我們項目考慮用戶可以實現多種終端監控,那麼多個用戶都可以直接綁定到同一個設備上,那麼用戶與設備之間是如何交互的呢?比如,設備發送反饋需要考慮是發送給所有綁定用戶,還是發送給一個用戶。用戶如何通過pid找到綁定的設備id。

下面具體分析之:

  • 用戶——服務器——>設備:由於用戶只能將指令發送給綁定的設備,設備的pid保存在用戶gw框架的uid中,通過其找到用戶的id
//檢查用戶是否綁定了PID
$boundPID = Gateway::getUidByClientId($user_id);
//設備集session
$bed_sessions = Gateway::getAllClientSessions();
//檢查設備是否在線
foreach ($bed_sessions as $temp_client_id => $temp_sessions) 
{

   if(!empty($temp_sessions['PID']) && $temp_sessions['PID'] == $boundPID)
   {
   	return $temp_client_id;
   }			
}
  • 設備——服務器——>所有用戶:設備更新後的數據直接推送給所以綁定的用戶,發佈訂閱模式,使用Gateway::sendToUid

什麼是發佈訂閱模式:觀察者模式和“發佈-訂閱”模式有區別嗎?

具體實現原理詳見gw手冊中的介紹:這裏

  • 設備——服務器——>單個用戶:點對點應答模式(這裏具體分析下

應答模式:用戶發送一個合法指令給設備,設備解析後必須應答該指令發送反饋給原用戶。(不合法的指令服務器會直接拒絕不轉給設備,但也會給原用戶發送相應的服務器反饋

由於用戶對設備來說是無狀態的,設備就不知道發來的消息是哪個用戶,無法直接點對點回復。那麼怎麼實現?

首先前提是我們的設備在收到指令後才發送反饋消息,是典型的點對點中的應答模式。

一種方式是:發送給設備的消息字段中包括用戶的uid或id,設備發送反饋消息攜帶該uid或id,服務器就知道了該發給哪個用戶。

另一種方式:我們的設備本質上是資源互斥。在同一時刻只能接收唯一用戶的指令,當工作完後再發送相應的反饋。那麼可以:①在發送指令時,給設備進程設置sessionGateway::setSession(array('work01_userid'=>xxx)對應用戶的id,②工作完成後釋放設備進程中該sessionGateway::updateSession,③當有其他用戶想要操作設備時,首先檢查設備進程中該操作的session釋放被釋放,沒有釋放則直接反饋正在工作的服務器反饋。

如果你的需求不是互斥的應答模式,選擇第一種。我們這用第二種。

上面用戶與設備的交互都經過服務器,服務器接收到消息解析後決定怎麼處理,發送給哪個、全部用戶或哪個設備。但是,如果服務器處理髮來的消息不需要轉發,而是直接反饋給原設備或原用戶(服務器反饋)呢:使用Gateway::sendToCurrentClient

——>4、心跳檢測

心跳檢測在設備與服務器交互中也是不可忽略的一部分。由於設備自定義協議部分沒有完善的斷開連接檢測機制,設備掉電的等情況下,服務器端無法短時間內知道設備的連接情況(長時間後可以通過TCP層知道連接斷開)。爲此,需要心跳檢測的設置。

gw框架中有心跳檢測的詳細介紹和幾種用法:這裏

我們項目中是通過設備定時向服務器發送ping數據包,如果超過一定時間沒有發送,則認爲設備斷開。(主要考慮到設備同時接受和發送數據存在丟失的情況,設置優先級)

——>5、日誌

當後端以守護進程的方式運行時,需要將後端的運行日誌輸出到文件中保留。

這裏採用monolog日誌類庫(這裏)。

按日期生成日誌文件。(日誌位置:Applications\SmartBed\log)

——>好了,我們的業務邏輯就分析到這裏了~~


前端設計

我們前端的特徵:

  • 使用AngularJs+jQuery WEUI搭建移動版單頁面應用程序
  • UI與業務的分離,使用angular-websocket接發消息,實現UI的異步刷新
  • 實現斷開重連功能(手機在鎖屏後會斷開ws連接)
  • 實現掃描二維碼綁定設備功能(設置綁定功能的路由url)

——>0、前端的選擇

簡單介紹下前端的選擇和設計。由於我們的後端已經預留好了用戶端的應用層協議(WebSocket)和消息類型(Json的形式),可以支持多種終端的開發:web端,手機端,PC端。

從技術的角度來說以上都可以實現,開發中主要關注幾點:

  • ①數據的解析和發送
  • ②UI界面的設計
  • ③數據與UI的交互

從用戶的角度來說,網頁版的輕應用(web app)無疑是首選,也是當前物聯網前端開發的趨勢。

  • 只要是支持瀏覽器的各種終端上都可以運行。
  • 開發方便,有各種現成的前端框架和UI
  • 前端業務放在服務器中,更新維護方便

爲此,我們web app的搭建選擇AngularJS(web後臺)和 jQuery WEUI(web前端)。


——>1、爲什麼選擇AngularJS?

首先,需要了解下什麼是單頁面應用。

單頁應用程序 (SPA) 是加載單個HTML 頁面並在用戶與應用程序交互時動態更新該頁面的Web應用程序。 瀏覽器一開始會加載必需的HTML、CSS和JavaScript,所有的操作都在這張頁面上完成,都由JavaScript來控制

通俗一點說就是指只有一個主頁面的應用,瀏覽器一開始就必須加載所有的html, js, css。所有的頁面內容都包含在這個所謂的主頁面中。但在寫的時候,還是會分開寫(頁面片段),然後在交互的時候由路由程序動態載入,單頁面的頁面跳轉,僅刷新局部資源。

我們項目中,處理websocket的業務邏輯(連接ws,接收/發送數據)都放在js中,在加載首頁面時就建立起ws連接。但是,如果使用傳統的多頁面,每次跳轉頁面都行需要重新加載js,也就需要重新建立ws連接。

這就是項目中使用單頁面應用的原因。其中,AngularJS就是一款優秀的前端JS框架, 使得開發現代的單一頁面應用程序變得更加容易。由於自己在前端也是小白一枚,就不做過多介紹了。

主要說下我們項目中選擇angularJS的優勢在於:

  • 單頁面:保證ws的一次連接不斷開
  • MVC模式:UI與數據分離,UI可以異步處理數據的更新
  • 依賴注入:業務邏輯的處理部分注入到factory服務中,實現鬆耦合
  • 路由機制:實現多視圖的單頁Web,同樣可以通過不同的 URL 訪問不同的內容

其中,最主要的關注就是業務邏輯的處理部分:

  • websocket數據的處理(ws連接、數據接收/發送)?
  • UI如何做到數據的異步刷新?

在項目中我們用到了angular-websocket的開源庫,結合項目中的業務邏輯,寫了一篇詳細的用法介紹和心得體會:angular-websocket學習筆記(這裏)


——>2、jQuery WEUI

Query WeUI 是專爲微信公衆賬號開發而設計的一個簡潔而強大的UI庫,包含全部WeUI官方的CSS組件,並且額外提供了大量的拓展組件,豐富的組件庫可以極大減少前端開發時間。

基本上需要的UI界面和動畫插件都有,並且開發起來也易上手。傳送門:jQuery WeUI


總結

護理牀的項目算是自己的第一次,o( ̄︶ ̄)o。

花了不少的時間,一邊學習一邊上手開發。從開始的安卓開發:手機局域網內的連接監控(這裏),到後端設備管理平臺的搭建。
收穫:雖然只算的上是一個學習研究的項目,離最終的目標還差的遠,其中很多功能也不夠完善和穩定。但這期間從後端到前端也學到了很多技術,知道項目開發的一些基本知識,鍛鍊了擼代碼的能力。

不足之處:第一,因爲都是自己一個人在折騰,視野和技術的選擇會不夠好。第二,目前只是做到拿來即用,技術棧的研究不夠深入。後面多看看源碼和技術細節,知其然知其所以然。

僅此附上項目開發的心路歷程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章