EMQ 是一款基于高并發的 Erlang/OTP 語言平臺設計,支持百萬級連接和分布式集群,發布/訂閱模式的開源MQTT消息服務器。
EMQ 單節點支持100萬并發MQTT連接峰值負載,超過了絕大多數的后端服務對消息的處理能力。因此往往需要多個后端服務節點來分擔。但是基于的 MQTT 3.1.1 協議標準,如果多個后端服務節點訂閱了相同的 topic,來自這些 topic 消息會被同時派發給所有的后端服務。為了解決這樣的問題,EMQ 提供了共享訂閱這個機制。
共享訂閱 (Shared Subscription) 是指在多訂閱者間采用分組負載平衡方式派發消息
EMQ 提供了如下兩種共享訂閱的方式:
- $queue/ 隊列共享訂閱
- $share/<group/ 分組共享訂閱
隊列共享訂閱是指以 $queue/ 開頭的 topic 例如 $queue/topic1,如果有多個客戶端同時訂閱了,EMQ 會把發送到 topic1 的消息以負載均衡的方式派發給所有客戶端,保證一條消息只會派發給一個客戶端。需要注意前綴 $queue 只適用于訂閱時,發布消息時不需要。
分組共享訂閱是一個更加靈活的方式,實現方式為所有以 $share/<group>/ 開頭的的 topic 會以 group 來分組進行消息的負載均衡派發。例如有4個客戶端依次訂閱了$share/group1/topic1,$share/group1/topic1,$share/group2/topic1 和 $share/group1/topic1。所有發送到 topic1 的消息會被同時派發到分組 group1 和 group2,但是兩個分組中的每次都只會有一個客戶端收到消息。同樣前綴$share/<group>/ 只適用于訂閱時,發布消息時不需要。
以上都是基于單個 EMQ 節點的共享訂閱方法,在多個 EMQ 節點組成的集群環境下也能實現共享訂閱的消息只會到達一個或者每組中的一個訂閱客戶端嗎?
很遺憾,答案是不能。雖然 EMQ 能簡單的通過 HAProxy 來搭建分布式集群,但是直到最新的 2.3.11 版本,都沒有支持集群環境下的共享訂閱。如果在集群中使用共享訂閱,在消息到達時,集群機制會把消息發送到每個 EMQ 節點上,隨后每個 EMQ 節點會把消息以負載均衡的方式派發給與自己建立連接的訂閱客戶端。換句話說,集群環境中的每個節點上都會有一個或者每組一個訂閱客戶端收到共享訂閱的消息。
幸運的是,結合 EMQ 提供的本地訂閱機制可以實現,可實現偽集群下的共享訂閱。
本地訂閱(Local Subscription) 只在本節點創建訂閱與路由表,不會在集群節點間廣播全局路由
本地訂閱的方式跟隊列共享訂閱很相似,都是在普通的 topic 前加上固定的前綴。本地共享訂閱的前綴是 $local/。
使用本地共享訂閱以后,來自訂閱的 topic 的消息只會在消息發布客戶端連接的 EMQ 節點上進行派發,也就做到了只有這個節點上一個或者每組中的一個共享訂閱客戶端收到消息。
如果消息發布客戶端所在的 EMQ 節點上沒有訂閱客戶端,由于本地訂閱的特性,其他節點上訂閱客戶端也無法收到消息。因此必須保證 EMQ 集群中的每個節點上都至少得一個本地共享訂閱客戶端才能達真正意義上的共享訂閱。
由于該實現機制要求每個 EMQ 節點上都有共享訂閱客戶端,結合業務就是要求每個 EMQ 節點上至少有一個消息處理服務連接,考慮備份則每個節點需要至少兩個服務。擴容 EMQ 節點需要同時對服務節點相應擴容,因而稱該實現方式為偽集群環境下的共享訂閱。
EMQ 官方團隊已經表明會在下次大版本更新時支持集群下的共享訂閱,因此如果目前單 EMQ 節點足夠支撐業務需求可延緩集群化時間,等待下個大版本更新的發布。