標籤:bsp 線程 process rom 移動 ble let img for
基於Http協議訂閱發布系統設計
--物聯網系統架構設計
1,訂閱發布(subscriber-publisher) 訂閱發布模式最典型的應用情境就是訊息系統的設計。在訊息系統的架構中,訊息的寄件者稱作(publisher),訊息的接收者稱作(subscriber),參見wikipedia: Publish–subscribe pattern。整個訊息系統的架構可以用如1來描述:
圖1 由圖1可知訊息系統主要包括3個組件: 發行者,訂閱者和訊息代理(Broker),而整個訊息系統的核心即是Broker,而目前就業務能力而言Broker的實現痛點主要在於它的輸送量。拿手機訊息推送舉例,在當前的移動互聯時代,就我們很常見的大多數app使用者數基本都是百萬層級以上(流行app基本是千萬層級),這意味著Broker至少要能支援百萬台裝置的訂閱,使用單台伺服器做Broker顯然不能解決問題。而在物聯網時代,訂閱者將不再只有手機,訂閱者可以是任何電子裝置,這種情境的層級將是手機數量的百倍。
2,Mqtt協議的發布訂閱系統實現方案
2.1,Mqtt協議 根據官方的定義,mqtt協議即是
machine-to-machine (M2M)的連線協定,該協議就是為發布訂閱模式設計的非常輕量的訊息傳輸協議。具體參見:http://mqtt.org/從mqtt協議定義可知,該mqtt就是為發布訂閱系統而設計,並且非常輕量。
2.2,實現方案 實現一套完整的發布訂閱系統,主要就是兩個組件(client和broker)一個協議規範(mqtt)。 目前流行的開源mqtt client實現是paho(http://eclipse.org/paho); 流行的開源mqtt broker實現包括 apache apollo 和 Eclipse Mosquitto(http://eclipse.org/mosquitto), mosquitto的優點是非常輕量,使用一台樹莓派(或路由器)這樣的小型裝置足夠服務一個家庭的裝置串連。
2.3, 架構設計 發布訂閱的服務系統架構非常簡單,基本都遵照圖1的基本架構模式。對於一個家庭的物聯網應用,如果裝置僅想要在區域網路內訪問,則broker只需要安裝在(基於NanoPi或RasPi開發的)小型的裝置中或者直接整合到路由器中。當然對於真正的物聯網應用,我們還是希望裝置可以通過互連網就可以管理和控制,所以很多broker實際應當在互連網伺服器中。
2.4, Mqtt協議的訂閱發布系統互動原理 首先引用一下開源項目paho提供的python版用戶端執行訂閱和發布動作的demo,代碼非常簡短
1 #susbscriber 2 import paho.mqtt.client as mqtt 3 4 # The callback for when the client receives a CONNACK response from the server. 5 def on_connect(client, userdata, rc): 6 client.subscribe("$SYS/#") 7 8 # The callback for when a PUBLISH message is received from the server. 9 def on_message(client, userdata, msg):10 print(msg.topic+" "+str(msg.payload))11 12 client = mqtt.Client()13 client.on_connect = on_connect14 client.on_message = on_message15 client.connect("iot.eclipse.org", 1883, 60)16 17 # Blocking call that processes network traffic18 client.loop_forever()View Code
Subscriber: 從訂閱者用戶端代碼可知,訂閱者只需做2個動作(串連broker和建立迴圈等待的長串連)和提供2個介面函數(訂閱請求函數和處理broker響應結果的函數)。基本要素無非請求串連、訂閱指定topic訊息、和處理響應結果,但loop_forever()是一個無限迴圈,這意味著用戶端和borker之間保持著一個socket長串連,所以從這裡可以認識到broker的瓶頸之一便是能處理多少個這樣的長串連。
1 #publisher2 import paho.mqtt.client as mqtt3 4 client = mqtt.Client()5 client.connect("iot.eclipse.org")6 client.loop_start()7 res = mqttc.publish("$SYS/#", "HELLO")8 client. loop_stop(force=False) Publisher: 從發行者用戶端代碼可知,發行者操作比訂閱者更加簡單,基本要素無非是建立串連、向broker發布指定topic訊息,忽略結果響應處理過程。 subscriber和publisher的互動邏輯本質是基於tcp協議的socket實現,對於server端的socket開啟mqtt協議連接埠,並開啟一個非同步線程來持續監聽連接埠,等待client端(subscriber和publisher )的socket發出mqtt請求,client端的subscriber的mqtt請求有些不一樣,那就是subscriber的socket實際和server一直保持長串連,隨時等待server那邊推送過來的訊息,直到串連關閉 。所以拋開細節處理問題,完全可以使用netty架構,基於mqtt協議很快的開發出一套server和client端的應用。
3,http協議broker設計實現
圖2 訂閱發布系統Broker設計 http協議和mqtt協議比較: 優點:http在互連網時代得到最廣泛的應用, 充分檢驗了它的有效性和穩定性,充分的社區支援和成熟的開源資源可用 缺陷:相對mqtt協議太重,對網路要求更高,直接基於http無法實現發布訂閱(http是單工協議,需要依賴websocket、servlet3.0等技術實現雙工) 本文是使用servlet3.0的技術實現基於http協議的發布/訂閱系統broker, 圖2所示即為物聯網broker系統設計架構。後台broker分成兩大模組:發布中心(使用者和裝置)和訂閱中心(使用者和裝置),以及事件匯流排。這樣的設計或許會有疑惑,為什麼不直接抽象成事件的發布和訂閱中心,如此不久和mqtt broker一致了嗎? 的確,既然是使用http協議實現,那為什麼要完全仿照mqtt協議的模式呢,而且我們要設計的實際是一個“物聯網的業務系統“而不是一個“中介軟體“,所以如果你換了一個業務情境,你又得重新設計系統,而恰巧基於http協議servlet應用正是為業務系統提供了豐富的開源資源。 下面詳細解釋使用者發布中心和訂閱中心的設計,因為在物聯網的應用情境中,主要業務互動邏輯是圍繞使用者和裝置之間做publish和subscribe. 使用者發布中心(publisher): 在物聯網情境中使用者充當了核心業務的publisher,對於broker的發布中心,接收到所有的前端使用者請求過來的資料都將被封裝成event在broker的內部系統中由發布中心廣播到訂閱中心。以摩拜單車為例,app是publisher的終端,摩拜單車的核心商務邏輯就是開鎖指令和一系列的交易邏輯。就開鎖動作而言,發布中心收到開鎖event,在publish這個event之前,針對這個event不同業務情境或許有不同的業務需求,典型情境有:該事件是否需要群發、該事件是否需要定時功能,該事件是否需要可靠發布。特別的,對於事件的可靠發布,在交易類系統中屬於必備要求。拿摩拜單車來說,開鎖指令發出後就會開始計時計費和扣錢,這時候就需要依賴broker在應用程式層面對資料做事務保證,而不能依賴基礎系統服務的穩定。 訂閱中心(subscriber): 對於訂閱中心(無論是使用者或裝置)的設計,完全遵照table或key-value的資料結構來設計,也即是對於每一個請求,broker都將為其關聯一個handler以及和其對應id標識。當事件被發布到訂閱中心,訂閱中心的processor便會用事件ID(或唯一標識的裝置ID)去查詢對應的handler,並作結果響應。由於是基於http協議,所以在具體實現時需要依賴servlet3.0或websoket技術。
4,領域建模
4.1 發布中心領域建模 發布中的核心功能是發布事件,因此Event是發布中心的核心領域對象。在圖2中已經闡明,事件發布所需要實現的準系統要素,Event設計也就主要是達到第3部分所描述功能。圖3 發布中心領域模型抽象 在圖3中可知,AbstractEvent即是Event的頂層的Entity抽象設計。因為發布中心可能會發布多種不同類型的Event,所以AbstractEvent必須有EventType屬性來表述事件的類型。無論是那種類型的Event實際都是一個Entity,既然是Entity就意味著有自己的ID,EventId作為event的唯一識別碼,需要有一個明確的說明的是EventId表示意義實際相當於topic,這就是說不是每發布一個Event就會產生一個新的EventId。例如在摩拜單車的應用中,就開鎖這一類事件,對於每一輛單車,都有對應一個唯一的EventId。對於之前第3部分提到的關於事件需要實現周期、延時以及可靠發布,AbstractEvent定義了cronExpression和deliveryStatus屬性,其中cron運算式可以非常簡潔的描述和實現周期和延時的事件設定, 而deliveryStatus則需要使用狀態來保證分布式網路環境下事件動作的事務。此外,定義GroupEvent是為瞭解決第3部分中提到的發布一個事件,響應多台裝置。
4.2 訂閱中心領域建模 訂閱中心領域核心抽象是Handler,每一個handler對應為一個訂閱http request。每一個訂閱請求handler都持有其希望響應的EventId,攜帶的業務資料以及結果響應的回調方法。訂閱者期望的是當EventId標識的event發生時,可以立刻收到對應的事件響應,也即是說訂閱http request是作為一個保持長時間等待的網路連接。因此所有的handler應當有一個holder將其緩衝起來管理,這就是單例模式的HandlerHolder存在的意義。對於HandlerHolder在對handler緩衝策略可以有兩種選擇:1, 以table形式緩衝;2,以map形式緩衝。相較兩種緩衝策略各有優缺點,table形式節約儲存但尋找代價高(可有序存放提高速度),map形式尋找快但耗儲存,但無論那種形式緩衝都可以通過分級緩衝來提高緩衝能力(例如一級緩衝簡要資料在系統記憶體,二級緩衝主要資料在redis等緩衝系統)。 在圖4中的狀態圖描述了(使用者和裝置)訂閱中心以event驅動的handler轉移流程。初始時刻,裝置發起訂閱CommandEvent請求,等待發布中心收到使用者發過來的CommandEvent請求,此時發布中心會去判斷該事件是否需要記錄事件交付狀態,如需要得到裝置響應的OKEvent,則會去訂閱中心產生對應handler。此時,響應給裝置的handler將攜帶deliveryStatus=Waiting 標識,等待裝置返回確認結果。隨後,裝置返回的確認響應即可通過發布中心發布OKEvent響應使用者處理結果。(實際處理流程應當更複雜,因為沒有考慮異常情況,如裝置沒有收到響應結果、備響應結果丟失等,這些都需要做一些補償策略)
圖4 訂閱中心領域建模抽象
基於Http協議訂閱發布系統設計