在上一篇文章《MQTT Broker 集群詳解(一):負載均衡》中,我們簡單介紹了 MQTT 負載均衡:負載均衡既可以應用于傳輸層,也可以用于應用層。在本文中,我們將詳細介紹應用層負載均衡,其中最有趣的部分:粘性會話(sticky-session)。
本文由兩部分組成,第一部分將介紹 MQTT 會話,以及在分布式 MQTT Broker 集群中處理會話面臨的挑戰;第二部分是通過在 EMQX 4.3 集群前面配置 HAProxy 2.4 負載均衡器,帶讀者親自體驗如何充分利用粘性會話實現負載均衡。
MQTT 會話為了持續接收消息,MQTT 客戶端通常會連接至 MQTT Broker 進行訂閱并保持長期連接。由于網絡問題或客戶端軟件維護等原因,連接可能會中斷一段時間,這并不罕見,但客戶端通常希望在重新連接成功后仍然能接收到中斷期間漏收的消息。
因此,為客戶端提供服務的 MQTT Broker 應該為客戶端保持會話(根據客戶端的請求,將「Clean-Session」標志設置為 false)。此時,即使客戶端斷開連接,訂閱者當前訂閱的主題以及傳遞給這些主題的消息(QoS1 和 2)等也會由消息服務器(broker)保留。
當具有持久會話的客戶端重新連接時,它不需要重新訂閱主題,消息服務器應該將所有未發送的消息發送給該客戶端。
我們之前寫過一篇關于 MQTT 會話的文章,如果您對 MQTT 會話的技術細節感興趣,可以通過閱讀這篇文章做進一步了解。
會話接管當 MQTT Brokers 形成集群時,事情會變得更加復雜。從客戶端的角度來看,要連接的服務器不止一個,很難知道哪個服務器最適合連接。我們需要網絡中的另一個關鍵組件:負載均衡器。負載均衡器成為整個集群的接入點,并將客戶端的連接路由到集群中的某一個服務器。
如果客戶端通過負載均衡器連接到服務器(例如,node1),然后斷開連接并稍后重新連接,則新連接可能會路由到集群中的不同服務器(例如,node3)。在這種情況下,node3 應該在客戶端斷開連接時開始向客戶端發送未發送的消息。
實現集群范圍的持久會話有很多不同的策略。例如,整個集群可以共享一個全局存儲來保存客戶端的會話。
然而,更具可擴展性的解決方案通常以分布式方式解決這個問題,即數據從一個節點遷移到另一個節點。這種遷移稱為會話接管。會話接管應該對客戶端完全透明,但它是有代價的,尤其是當有很多消息需要處理時。
粘性會話解決方案這里的「粘性」一詞指的是負載均衡器能夠在重新連接時將客戶端路由到之前服務器的能力,這可以避免會話接管。當有許多客戶端在同一時間重新連接時,或者在一個有問題的客戶端反復斷開連接并再次連接的情況下,這是一個特別有用的功能。
為了讓負載均衡器以「粘性」方式分派連接,服務器需要知道連接請求中的客戶端標識符(有時是用戶名)——這需要負載均衡器檢查 MQTT 數據包以查找此類信息。
一旦獲得客戶端標識符(或用戶名),對于靜態集群,服務器可以將客戶端標識符(或用戶名)散列到服務器 ID。或者為了更好的靈活性,負載均衡器可以選擇維護一個從客戶端標識符(或用戶名)到目標節點 ID 的映射表。
在下一節中,我們將演示 HAProxy 2.4 中的粘性表策略。
使用 HAProxy 2.4 實現粘性會話為了盡量減少先決條件,在這個演示集群中,我們將在 docker 容器中啟動兩個 EMQX 節點和一個 HAProxy 2.4。
創建 docker 網絡為了使容器彼此連接,我們為它們創建了一個 docker 網絡。
為了使節點彼此連接,應該在網絡名稱空間(test.net)中分配容器名稱和 EMQX 節點名稱。
啟動 node1使 EMQX 節點加入集群注意環境變量
EMQX_LISTENER__TCP__EXTERNAL__PROXY_PROTOCOL. 該變量是為TCP監聽器啟用二進制代理協議,以便服務器可以獲得客戶端的真實 IP 地址信息,而不是負載均衡器的 IP 地址。
如果一切按預期進行,應該打印輸出這樣的日志:
創建文件 /tmp/haproxy.config,內容如下:
在測試 docker 網絡中啟動 haproxy:
現在我們使用流行的 mosquitto MQTT 客戶端(也在 docker 中)對其進行測試。
我們啟動一個訂閱者(名為 subscriber1)訂閱 t/# 主題
然后從另一個客戶端向 t/xyz 發布一條 hello 消息
如果一切都按預期進行,訂閱者應該打印出 hello 消息。
檢查 HAProxy 中的粘性表我們還可以使用如下命令檢查在 HAProxy 中創建的粘性表。這需要 socat 命令,所以我們從 docker 主機運行它。
該命令應該打印當前連接,如下所示:
在這個例子中,客戶端 subscriber1 被固定連接到服務器 emqx2。
結語至此,我們可以了解到從客戶端的角度看,EMQX 集群是如何通過負載均衡器對外部提供服務的。
在本系列文章的后續內容中,我們將跟蹤一個 MQTT 消息從發布者到訂閱者的全過程,以便大家了解 EMQX 如何將它在集群中復制和轉發。敬請期待。
本系列中的其它文章