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