我最喜歡的進程之間通訊方式-消息總線


道哥的第 020 篇原創

1、Linux 系統中的進程之間通訊(IPC)

做爲一名嵌入式軟件開發人員來講,處理進程之間的通訊是很常見的事情。從通訊目的的角度來看,咱們能夠把進程之間的通訊分紅 3 種:linux

  1. 爲了進程的調度: 能夠經過信號來實現;
  2. 爲了共享資源:能夠經過互斥鎖、信號量、讀寫鎖、文件鎖等來實現;
  3. 爲了傳遞數據:能夠經過共享內存、命名管道、消息隊列、Socket來實現。

關於上面提到的這些、操做系統爲咱們提供的通訊原語,網絡上的各類資料、文章滿天飛,在這裏就不囉嗦了。在這些方法中應該如何選擇呢?根據我我的的經驗,貴精不貴多,認真挑選三四樣東西就能徹底知足平常的工做須要。數據庫

咱們今天想討論的問題主要是第 3 個:傳遞數據,在上面這幾種傳遞數據的方法中,我最喜歡、最經常使用的就是 Socket 通訊編程

有些小夥伴可能會說:Socket 通訊就是 TCP/IP 的那一套東西,還須要本身管理鏈接、對數據進行組包、分包,也是挺麻煩的。json

沒錯,Socket 通訊自己的確須要手動來處理這些底層的東西,可是咱們能夠給 Socket 穿上一層「外衣」:利用 MQTT 消息總線,在系統的各進程之間進行數據交互,下面咱們就一一道來。ubuntu

2、基於 Socket 通訊的優勢

這裏我就不本身發揮了,直接引用陳碩老師的那本書《Linux 多線程服務端編程》這本書中的觀點(第 65 頁,3.4小節):設計模式

1. 跨主機,具備伸縮性

反正都是多進程了,若是一臺機器的處理能力不夠,就能用多臺主機來處理。把進程分散到同一臺局域網的多臺機器上,程序改改 Host:Port 配置就能繼續用。相反,文章開頭部分列出的那些進程之間通訊方式都不能跨機器,這就限制了可擴展性緩存

2. 操做系統會自動回收資源

TCP port 由一個進程獨佔,當程序意外退出時,操做系統會自動回收資源,不會給系統留下垃圾,程序重啓以後能比較容易地恢復。安全

3. 可記錄、可重現

兩個進程經過 TCP 通訊,若是一個崩潰了,操做系統會關閉鏈接,另外一個進程幾乎馬上就能感覺到,能夠快速 failover。固然應用層的心跳是必不可少的。(補充:操做系統自己對於 TCP 鏈接有一個保活時間,默認是 2 個小時,並且是針對全局的。)網絡

4. 跨語言

服務端和客戶端沒必要使用同一種編程語言。多線程

  1. 陳碩老師描述的是通用的 Socket 通訊,所以客戶端和服務端通常位於不一樣的物理機器上。
  2. 在嵌入式開發中,通常都是用同一種編程語言,所以,跨語言這個有點能夠忽略不計了。

3、MQTT 消息總線

1. MQTT 是一個通訊的機制

對物聯網領域熟悉的小夥伴,對於 MQTT 消息總線必定很是熟悉,目前幾大物聯網雲平臺(亞馬孫、阿里雲、華爲雲)都提供了 MQTT 協議的接入方式。

目前,學習 MQTT 最好的文檔是 IBM 的在線手冊https://developer.ibm.com/zh/technologies/messaging/articles/iot-mqtt-why-good-for-iot/。

這裏,我直接把一些重點信息列出來:

  1. MQTT協議輕量、簡單、開放和易於實現;
  2. MQTT 是基於發佈 (Publish)/訂閱 (Subscribe)範式的消息協議;
  3. MQTT 工做在 TCP/IP協議族上;
  4. 有三種消息發佈服務質量;
  5. 小型傳輸,開銷很小(固定長度的頭部是 2 字節),協議交換最小化,以下降網絡流量;

MQTT 消息傳輸須要一箇中間件,稱爲:Broker,其實也就是一個 Server。通訊模型以下:

  1. MQTT Broker 須要首先啓動;
  2. ClientA 和 ClientB 須要鏈接到 Broker;
  3. ClientA 訂閱主題 topic_1,ClientB 訂閱主題 topic_2;
  4. ClientA 往 topic_2 這個主題發送消息,就會被 ClientB 接收到;
  5. ClientB 往 topic_1 這個主題發送消息,就會被 ClientA 接收到;

基於 topic 主題的通訊方式有一個很大的好處就是解耦,一個客戶端能夠訂閱多個 topic,任何接入到總線的其餘客戶端均可以往這些 topic 中發送信息(一個客戶端發送消息給本身也是能夠的)。

2. MQTT 的實現

MQTT 只是一個協議而已,在 IBM 的在線文檔中能夠看到,有不少語言都實現了 MQTT 協議,包括:C/C++、Java、Python、C#、JavaScript、Go、Objective-C等等。那麼對於嵌入式開發來講,使用比較多的是這幾個實現:

Mosquitto;
Paho MQTT;
wolfMQTT;
MQTTRoute。

在下面,咱們會重點介紹 Mosquitto 這個開源實現的編譯和使用方式,這也是我在項目中使用最多的。

3. 在 MQTT 之上,設計本身的通訊協議

從上面的描述中能夠看出,MQTT 消息總線就是一個通訊機制,爲通訊主體提供了一個傳遞數據的通道而已。

在這個通道之上,咱們能夠根據實際項目的須要,發送任何格式、編碼的數據。在項目中,咱們最經常使用的就是 json 格式的純文本,這也是各家物聯網雲平臺所推薦的方式。若是在文本數據中須要包含二進制數據,那就轉成 BASE64 編碼以後再發送。

4、嵌入式系統中如何利用 MQTT 消息總線

從上面的描述中能夠看到,只要在服務端運行着一個 MQTT Broker 服務,每一個鏈接到總線的客戶端均可以靈活地相互收發數據。

咱們能夠把這個機制應用在嵌入式應用程序的設計中:MQTT Broker 做爲一個獨立的服務運行在嵌入式系統本地,其餘須要交互的進程,只要鏈接到本地的這個 Broker,就能夠相互發送數據了。運行模型以下:

每個進程只須要訂閱一個固定的 topic(好比:本身的 client Id),那麼其餘進程若是想要發送數據給它,就直接發送到這個 topic 便可。

1. 一個嵌入式系統的通訊框架

我以前開發過一個環境監測系統,採集大氣中的 PM2.五、PM10等污染物參數,在 Contex A8 平臺下開發,須要實現數據記錄(數據庫)、UI 監控界面等功能。

污染物的數據採樣硬件模塊是第三方公司提供的,咱們只須要經過該模塊提供的串口協議去控制採樣設備、接收採樣數據便可。最終設計的通訊模型以下:

  1. UI 進程經過消息總線,發送控制指令給採樣控制進程,採樣控制進程接收到後經過串口發送控制指令給採樣模塊;
  2. 採樣控制進程從串口接收採樣模塊發來的PM2.5等數據後,把全部的數據發送到消息總線上指定的 topic 中;
  3. UI 進程程訂閱該 topic,接收到數據後,顯示在屏幕上;
  4. 數據庫進程也訂閱該 topic,接收到數據後,把數據存儲在 SQLite 數據庫中;

在這個產品中,核心進程是採樣控制進程,負責與採樣模塊的交互。經過把 UI 處理、數據庫處理設計成獨立的進程,下降了系統的複雜性,即便這 2 個進程崩潰了,也不會影響到核心的採樣控制進程。

好比:若是 UI 進程出現錯誤崩潰了,會馬上重啓,啓動以後經過緩存信息知道此刻正在執行採樣工做,因而 UI 進程馬上鏈接到消息總線、進入採樣數據顯示界面,繼續接收、顯示採樣控制進程發出的PM2.5等數據

這個通訊模型還有另一個有點:可擴展性

在項目開發的後期,甲方說須要集成一個第三方的氣體模塊,用來採集大氣中NO、SO2等參數,通訊方式是 RS485。

此時擴展這個功能模塊就異常簡單了,直接寫一個獨立的氣體參數進程,接入到消息總線上。這個進程經過 RS485,從第三方氣體模塊接收到NO、SO2等氣體參數時,直接往消息總線上的某個 topic 一丟,UI進程、數據庫進程訂閱這個 topic,就能夠馬上接收到氣體相關的數據了。

此外,這個設計模型還有其餘一些優勢

  1. 並行開發:每一個進程能夠由不一樣的人員並行開發,只要相互之間定義好通訊協議便可;
  2. 調試方便:因爲發送的數據都是 manual readable,在開發階段,能夠在 PC 機上專門寫一個監控程序,接入到嵌入式系統中的 MQTT Broker 以後,這樣就能夠接收到全部進程發出的消息;
  3. 通訊安全:在產品 release 以後,爲了防止其餘人偷聽數據(好比 2 中的調試進程),能夠爲 MQTT Broker 指定一個配置文件,只能容許本地進程(127.0.0.1)鏈接到消息總線上。

2. 稍微複雜一點的通訊模型

在剛纔描述的嵌入式系框架設計中,每個進程都是運行在本地的,全部的消息也都是在系統內進行收發。那麼,若是須要把數據傳輸到雲端、或者須要從雲端接收一些控制指令,又該如何設計呢?

加入一個 MQTT Bridge 橋接模塊便可!也就是再增長一個進程,這個進程同時鏈接到雲端的 MQTT Broker 和本地的 MQTT Broker,通訊模型以下:

  1. MQTT Bridge 接收到雲端發來的指令時,轉發到本地的消息總線上;
  2. MQTT Bridge 接收到本地的消息時,轉發到雲端的消息總線上。

5、Mosquitto: 一個簡單的測試代碼

上面的內容主要討論的是設計的思想,具體到代碼層面,我通常使用的是 Mosquitto 這個開源的實現。

在 Linux 系統中安裝、測試都很是方便,下面就簡單說明一下。

1. 直接經過 apt 來安裝、測試

能夠參考這個文檔(https://www.vultr.com/docs/how-to-install-mosquitto-mqtt-broker-server-on-ubuntu-16-04)來安裝測試。

(1) 安裝

sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
sudo apt-get update
sudo apt-get install mosquitto
sudo apt-get install mosquitto-clients

(2) 測試

mosquitto broker 在安裝以後會自動啓動,能夠用 netstat 查看 1883 端口來確認一下。

接收端:鏈接到 broker 以後,訂閱 "test" 這個 topic。

mosquitto_sub -t "test"

發送端:鏈接到 broker 以後,往 "test" 這個 topic 發送字符串 「hello」。

mosquitto_pub -m "hello" -t "test"

當發送端執行 mosquitto_pub 時,在接收端的終端窗口中,就能夠接收到 「hello」 這個字符串。

2. 經過源碼來手動編譯、測試

經過 apt 來安裝主要是用來簡單的學習和測試,若是要在項目開發中使用 Mosquitto,確定須要手動編譯,獲得頭文件和庫文件,而後複製到應用程序中使用。

(1) 手動編譯、安裝 Mosquitto

個人開發環境是:

  1. 編譯器:gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
  2. Mosquitto 版本: mosquitto-1.4.9

mosquitto-1.4.9 能夠到官方網站下載,也能夠從文末的網盤中下載,你也能夠嘗試更高的版本。

編譯、安裝指令:

make
make install prefix=$PWD/install

成功安裝以後,能夠在當前目錄的 install 文件夾下看到輸出文件:

  1. bin:mqtt 客戶端程序;
  2. include:應用程序須要 include 的頭文件;
  3. lib:應用程序須要連接的庫文件;
  4. sbin:mqtt broker 服務程序。

在編譯過程當中,若是遇到一些諸如:ares.h、uuid.h 等依賴文件找不到的錯誤,只須要經過 apt 指令安裝響應的開發包便可。

(2) 最簡單的 mosquitto 客戶端代碼

在 mosquitto 源碼中,提供了豐富的 Sample 示例。若是你不樂意去探索,能夠直接下載文末的這個網盤中的 Demo 示例程序,這個程序鏈接到消息總線上以後,訂閱 「topic_01」 這個主題。固然,你也能夠修改代碼去發送消息(調用:mosquitto_publish 這個函數)。

進入 c_mqtt 示例代碼目錄以後,能夠看到已經包含了 bin、include 和 lib 目錄,它們就是從上面(1)中安裝目錄 install 中複製過來的。

執行 make 指令以後,便可編譯成功,獲得可執行文件: mqtt_client

測試過程以下:

Step1: 啓動 MQTT Broker

在第 1 個終端窗口中,啓動 sbin/mosquitto 這個 Broker 程序。若是你在上面測試中已經啓動了一個 broker,須要先 kill 掉以前的那個 broker,由於它們默認都使用 1883 這個端口,沒法共存。

Step2: 啓動接收端程序 mqtt_client

在第 2 個終端窗口中,啓動 mqtt_client 也就是咱們的示例代碼編譯獲得的可執行程序,它訂閱的 topic 是 「topic_01」。

./mqtt_client 127.0.0.1 1883

參數 1: Broker 服務的 IP 地址,由於都是在本地系統中,因此是 127.0.0.1;
參數 2: 端口號,通常默認是1883。

Step3: 啓動發送端程序 bin/mosquitto_pub

在第 3 個終端窗口中,啓動 bin/mosquitto_pub,命令以下:

./mosquitto_pub -h 127.0.0.1 -p 1883 -m "hello123" -t "topic_01"

參數 -h:Broker 服務的 IP 地址,由於都是在本地系統中,因此是 127.0.0.1;
參數 -p:端口號 1883;
參數 -m:發送的消息內容;
參數 -t:發送的主題 topic。

此時,能夠在第 2 個終端窗口(mqtt_client)中打印出接收到的消息。

6、總結

這篇文章主要介紹了嵌入式系統中的一個設計模式:經過消息總線來實現進程之間的通訊,並介紹了 Mosquitto 這個開源實現。

在實際的項目中,還須要更加嚴格的權限控制,好比:在接入消息總線時提供用戶名、密碼、設備證書,客戶端的名稱必須知足指定的格式,訂閱的 topic 必須符合必定的格式等等。

在下一篇文章中,咱們繼續討論這個話題,給出一個更具體、更實用的 Demo 例程。

7、資源下載

1. mosquitto-1.4.9.tgz

連接:https://pan.baidu.com/s/1izQ3dAlGbHiHwDvKnOSfyg
密碼:dozt

2. Mosquitto Demo 示例代碼

連接:https://pan.baidu.com/s/1M-dU3xapNbKyk2w07MtDyw
密碼:aup3


不吹噓,不炒做,不浮誇,認真寫好每一篇文章!
歡迎 轉發、分享給身邊的技術朋友,道哥在此表示衷心的感謝! 轉發的 推薦語已經幫您想好了:

道哥總結的這篇總結文章,寫得很用心,對個人技術提高頗有幫助。好東西,要分享!


【原創聲明】

做者:道哥(公衆號: IOT物聯網小鎮)
知乎:道哥
B站:道哥分享
掘金:道哥分享
CSDN:道哥分享

轉載:歡迎轉載,但未經做者贊成,必須保留此段聲明,必須在文章中給出原文鏈接。

關注+星標公衆號,不錯過最新文章



推薦閱讀

C語言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹
一步步分析-如何用C實現面向對象編程
提升代碼逼格的利器:宏定義-從入門到放棄
原來gdb的底層調試原理這麼簡單
利用C語言中的setjmp和longjmp,來實現異常捕獲和協程
關於加密、證書的那些事
深刻LUA腳本語言,讓你完全明白調試原理

相關文章
相關標籤/搜索