MQTT協議是爲大量計算能力有限,且工做在低帶寬、不可靠的網絡的遠程傳感器和控制設備通信而設計的協議。linux
一、使用發佈/訂閱消息模式,提供一對多的消息發佈,解除應用程序耦合redis
二、對負載內容屏蔽的消息傳輸。算法
三、使用TCP/IP提供網絡鏈接:sql
主流的MQTT是基於TCP鏈接進行數據推送的,可是一樣有基於UDP的版本,叫作MQTT-SN。這兩種版本因爲基於不一樣的鏈接方式,優缺點天然也就各有不一樣了數據庫
四、有三種消息發佈服務質量:緩存
[1] 「至多一次」,消息發佈徹底依賴底層TCP/IP網絡。會發生消息丟失或重複:安全
這一級別可用於以下狀況,環境傳感器數據,丟失一次讀記錄無所謂,由於不久後還會有第二次發送。這一種方式主要普通APP的推送,假若你的智能設備在消息推送時未聯網,推送過去沒收到,再次聯網也就收不到了。服務器
[2] 「至少一次」,確保消息到達,但消息重複可能會發生:網絡
這一種方式比較雞肋,在個人想象中沒能想到這種質量的發送在常規的APP開發中有什麼用處。數據結構
[3] 「只有一次」,確保消息到達一次:
這一級別可用於以下狀況,在計費系統中,消息重複或丟失會致使不正確的結果。這種最高質量的消息發佈服務還能夠用於即時通信類的APP的推送,確保用戶收到且只會收到一次。
六、使用Last Will和Testament特性通知有關各方客戶端異常中斷的機制:
Last Will:即遺言機制,用於通知同一主題下的其餘設備發送遺言的設備已經斷開了鏈接。
Testament:遺囑機制,功能相似於Last Will 。
因爲MQTT 協議具備開放、簡單、輕量、易於實
現等優勢, 所以他特別適用於低帶寬, 網絡不穩定,
網絡代價昂貴以及處理器和存儲器資源有限的嵌入式
設備和移動終端上.
客戶端(Client):
使用MQTT的程序或設備。客戶端老是經過網絡鏈接到服務端。它能夠
服務端(Server):
一個程序或設備,做爲發送消息的客戶端和請求訂閱的客戶端之間的中介。服務端
訂閱(Subscription):
訂閱包含一個主題過濾器(Topic Filter)和一個最大的服務質量(QoS)等級。訂閱與單個會話(Session)關聯。會話能夠包含多於一個的訂閱。會話的每一個訂閱都有一個不一樣的主題過濾器。
主題名(Topic Name):
附加在應用消息上的一個標籤,服務端已知且與訂閱匹配。服務端發送應用消息的一個副本給每個匹配的客戶端訂閱。
主題過濾器(Topic Filter:):
訂閱中包含的一個表達式,用於表示相關的一個或多個主題。主題過濾器可使用通配符。
會話(Session):
客戶端和服務端之間的狀態交互。一些會話持續時長與網絡鏈接同樣,另外一些能夠在客戶端和服務端的多個連續網絡鏈接間擴展。
控制報文(MQTT Control Packet):
經過網絡鏈接發送的信息數據包。MQTT規範定義了十四種不一樣類型的控制報文,其中一個(PUBLISH報文)用於傳輸應用消息。
整個服務器部分主要分紅三個層次. 第一層是
MQTT 消息推送broker, 負責完成協議底層的網絡通
信機制以及針對各類不一樣類型消息的收發機制; 第二
層由身份驗證模塊、ACL 控制模塊、自動訂閱模塊、
話題統計模塊以及狀態監控模塊組成, 是在底層通訊
機制的基礎上完善整個系統實際應用中所需的各項功
能; 第三層是數據存儲層, 爲第二層的各個模塊提供
數據的支持, 用於各項數據的統計與交互. 整個系統
框架如圖1 所示.
消息推送中間件
目前在各類平臺上對於MQTT協議有許多不一樣的
實現, 這裏所選擇的Mosquitto 是一款開源的基於C實
現的MQTT server/broker, 比較完整的實現了MQTT
協議中要求的各項基本功能, 能夠在Windows, Linux
以及其它類Unix 系統中編譯運行.
數據存儲層
在數據存儲層中, 有的數據是須要常常讀取的,
好比用戶的名稱、密碼、ID 以及用戶間的好友關係等;
有的數據是須要常常寫入和修改的, 好比某些話題的
訂閱數; 有的數據是不常常讀取或寫入的, 好比服務
器的運行狀態. 爲了提升效率, 對於須要常常讀取或
寫入的與用戶相關的數據, 用Redis 數據庫存儲, 對於
不常常讀取或寫入的與服務器狀態相關的數據, 用
MYSQL 數據庫存儲.
這裏的Redis 是一個基於key-value 的開源no-sql
數據庫, 它將數據緩存在內存中, 相對於傳統的關係
型數據庫來講, 性能上有很大提升, 特別適用於對訪
問速度和併發性要求比較高的狀況[6,7]. 同時Redis 也
支持數據的持久化, 而且支持list, set, hash 等多種不一樣
的數據結構. 在這裏採用Redis 來存儲每一個用戶的用
戶名密碼等相關信息和與話題有關的數量統計, 以處理大併發量下的用戶訪問請求.
密碼驗證模塊
在客戶端向服務器發起鏈接請求的時候, 服務器
必須對其進行密碼驗證, 以決定是否接受該鏈接請求,
驗證過程如圖2 所示.
爲了增長數據庫的安全性, 在用戶註冊時須要對其
密碼加密後再將其密文存入數據庫, 這樣即便有人利用
非法手段侵入數據庫, 也沒法獲得用戶的真實密碼. 在
這裏採用的MD5 加密算法是一種散列加密算法[8], 任何
一個密碼通過MD5 的HASH 散列計算以後將會產生一
個128bit 的序列. 可是缺點是兩個相同的密碼會產生相
同的散列, 爲了彌補這個不足, 須要引入SALT 技術[9],
在用戶註冊時生成一個隨機數據, 與用戶密碼一塊兒進行
散列計算, 而後將獲得的密文和SALT 值一塊兒存入數據
庫中, 這樣就能夠保證即便用戶的密碼相同, 只要隨機
的SALT 值不一樣, 其密文就不相同.
在用戶發起鏈接請求時, 將用戶提交的密碼明文
與數據庫中的SALT 值一塊兒進行散列計算, 將所得密文
與數據庫中密文進行比對便可對用戶密碼進行驗證.
ACL 控制模塊
爲了規範用戶行爲, 須要對其話題的訂閱和發佈
進行權限控制. ACL(Access Control List)又稱爲訪問控
制列表, 是一種經過匹配關係對訪問權限進行控制,
以增強系統安全性的技術[10,11].
ACL 表採用的格式爲:
user <username>
[read/write] <topic>
或者
pattern [read/write] <topic>
第一種格式爲特定用戶的權限控制規則, 第二種
格式爲全部用戶的權限控制規則, read 表示具備訂閱
的權限, write 表示具備發佈的權限, topic 採用層級結
構組織, 檢查的時候從左至右各層依次進行匹配.
ACL 表賦予用戶應該具備的最小權限, 只要知足表中
的一項, 即表示驗證經過.
爲了知足移動社交網絡中的應用需求, 須要引入
通配符來表示用戶之間的關係, 在這裏, 用%u 表示用
戶自身的用戶名, %c 表示用戶自身的ID 號, %f 表示用
戶的單向關注的關係, %F 表示用戶互相關注的關係
(這裏咱們稱之爲好友), +表示話題中單層的通配, #表
示話題中若干層的通配, 一個典型的ACL 表項爲:
pattern read user/%F/presence/+
該規則表示該用戶對全部他的好友的presence 之
下的一級子話題具備訂閱的權限.
自動訂閱模塊
在系統的實際應用中, 用戶須要接收各類各樣的
推送話題, 例如系統的廣播通知、好友的上線提醒、好
友發送的即時消息等, 若是每次上線的時候都由客戶
端來對這些話題進行訂閱, 不但會影響客戶端的性能,
還會佔用移動終端的網絡資源, 尤爲是在用戶關係復
雜, 須要訂閱大量話題的時候. 所以, 服務器能夠在用
戶登陸成功以後爲用戶自動訂閱這些話題, 以減小網
絡上的數據交互. 自動訂閱模塊流程如圖3 所示.
自動訂閱支持ACL 控制模塊中提到的各項通配
符, 而且將%u 替換爲自身用戶名, %c 替換爲自身ID,
含有%f 和%F 的表項會被通配爲多個話題分別進行自
動訂閱. 一個典型的自動訂閱表項爲:
user/%F/presence/+ 1
表示每一個用戶上線時都會自動訂閱其好友的
presence之下一級的話題, QoS設爲1, 中間以空格分隔.
話題統計模塊
當大量用戶在一段時間內同時訂閱某一話題, 或
者向某一話題發佈消息時, 意味着這個話題是當前用
戶所關注的熱點. 所以, 對話題的統計有助於分析用
戶羣體的行爲方式以及當前社會生活中的熱點時事,
對於發掘用戶的潛在需求有重要的意義.
爲了提升消息推進服務器的性能, 話題的統計數
據記錄在redis 數據庫中, 以topic-count 的形式存儲,
當用戶在統計的話題上進行訂閱或者發佈的時候, 服
務器調用Redis 的INCR 或DECR 命令對統計數據進
行更新.
狀態監控模塊
要確保服務器長時間穩定的運行, 須要按期對系統運行的狀態進行監控. 一方面要監控服務器自己的系統信息, 如CPU 佔用率、內存佔用率、磁盤剩餘空間大小、網絡流量等等, 這些數據在linux 環境下能夠經過解析/proc 下相關文件的內容來獲取; 另外一方面需要監控消息推送服務的運行情況, 如活躍的用戶數、用戶訂閱話題的總數等. Mosquitto 提供了對這些信息進行統計的機制, 所得的數據將由服務器以話題的形式按期進行發佈, 監控模塊須要以客戶的身份登陸並訂閱相應話題以獲取相關信息, 採集到的結果插入到MYSQL 數據庫中, 以供服務器進行性能分析以預警.