從百萬到億級:EMQX 5.0 新架構(gòu)的利與弊
1.Mnesia:Erlang語言中的分布式數(shù)據(jù)庫
在EMQX 5.x版本之前,集群數(shù)據(jù)存儲采用的是Erlang/OTP自帶的實時分布式數(shù)據(jù)庫管理系統(tǒng)——Mnesia。Mnesia是用Erlang語言實現(xiàn)的,并且與Erlang緊密耦合,這也造就了它的獨特之處,它幾乎將Erlang變成了一種數(shù)據(jù)庫編程語言。Mnesia可以說是專為用Erlang編寫的工業(yè)級電信應(yīng)用程序而設(shè)計的,并提供了支持高容錯電信級系統(tǒng)所必需的常用功能。它試圖解決典型電信系統(tǒng)所需的所有數(shù)據(jù)管理問題,并具有許多傳統(tǒng)DBMS通常不具備的功能。其提供的特性主要包括:
- 快速實時的鍵值查找;
- 復雜的非實時性查詢;
- 分布式數(shù)據(jù)支持;
- 高容錯性;
- 復雜的數(shù)據(jù)對象。
Mnesia通常支持兩種數(shù)據(jù)訪問模式:本地模式和遠程模式。本地模式采用的是全連接、點對點的復制模式,即節(jié)點中的數(shù)據(jù)表會復制到集群所有節(jié)點中;而在遠程模式中,當要訪問的表沒有本地副本時,會通過RPC調(diào)用讀取遠程具有數(shù)據(jù)表副本的節(jié)點。本地模式訪問的缺點是集群擴展性差,且存在腦裂的風險,但優(yōu)點也顯而易見,因為集群中每個節(jié)點都擁有集群全量的數(shù)據(jù),故而可以通過本地查詢來提高檢索效率。
相對于遠程模式的網(wǎng)絡(luò)操作而言,本地讀取數(shù)據(jù)的延遲要比遠程模式的網(wǎng)絡(luò)延遲小幾個數(shù)量級。另外,這種實現(xiàn)方式也能提高集群的分布式容錯能力,只要保證集群中仍有存活的節(jié)點,集群數(shù)據(jù)就是全量的、安全的。所以在早期的EMQX實現(xiàn)中,默認使用的就是本地模式。尤其是在消息分發(fā)時,通過本地查詢Mnesia數(shù)據(jù)庫中的路由表數(shù)據(jù)快速定位到消息要投遞的節(jié)點,可以實現(xiàn)個位數(shù)毫秒的高效、低延時的消息分發(fā)操作。
2.Mnesia的弊端:復制帶來的開銷
如前文所述,由于Mnesia集群使用全網(wǎng)狀的連接架構(gòu),集群中每個節(jié)點都會與其它所有的節(jié)點建立連接,每個節(jié)點產(chǎn)生的事務(wù)也都被會復制到集群中的所有節(jié)點上。這就導致集群的整體可擴展性差:首先,集群中每增加一個節(jié)點,集群數(shù)據(jù)同步的開銷也會隨之增大,且由于網(wǎng)絡(luò)問題導致的集群腦裂的風險也會增加。其次,集群中每個節(jié)點都要能夠承載全量的集群數(shù)據(jù),相對于Mnesia這種經(jīng)常將數(shù)據(jù)存放在內(nèi)存中的應(yīng)用場景來說,服務(wù)器資源的投入也會跟著集群規(guī)模的擴展而增加,對機器配置和性能的要求也會越來越高。集群節(jié)點間的數(shù)據(jù)復制成本和服務(wù)器資源投入這兩個問題一直是限制集群擴展性的核心問題。
Mnesia 網(wǎng)狀拓撲架構(gòu)
3.Mria:從全網(wǎng)狀到單復制
為了解決Mnesia全網(wǎng)狀復制帶來的問題,EMQX 5.x版本中引入了新的數(shù)據(jù)層解決方案實現(xiàn)——Mria。Mria對Mnesia進行了封裝,其核心訴求是在實現(xiàn)數(shù)據(jù)的本地讀寫的基礎(chǔ)上,盡可能地減少集群節(jié)點復制的開銷。
Mria將原有的全網(wǎng)狀復制的Mnesia節(jié)點擴展成兩種不同的角色節(jié)點——核心節(jié)點(Core)和復制節(jié)點(Replicant)。核心節(jié)點與傳統(tǒng)的Mnesia節(jié)點行為類似,仍舊采用全網(wǎng)狀的復制模式,所有核心節(jié)點之間的事務(wù)仍會復制到其它核心節(jié)點上。復制節(jié)點則不直接參與Mnesia事務(wù)處理,而是連接到集群中某個核心節(jié)點上,被動地復制來自核心節(jié)點的數(shù)據(jù)更新。為此,核心節(jié)點還同時擁有另外一項重要的工作,即處理連接到自身的所有復制節(jié)點的數(shù)據(jù)處理。
由于復制節(jié)點不再參與集群中事務(wù)的同步工作,只有少數(shù)的核心節(jié)點會實時地同步事務(wù),而復制節(jié)點只是復制對應(yīng)核心節(jié)點的數(shù)據(jù),所以這種實現(xiàn)模式在復制節(jié)點可以擁有集群全量數(shù)據(jù)以實現(xiàn)高效的本地數(shù)據(jù)檢索的前提下,同時能夠減少整個集群的事務(wù)同步開銷。借助于復制節(jié)點的特性,當更多的設(shè)備需要接入到集群中時,只需要相應(yīng)地擴展復制節(jié)點的數(shù)量,讓這些節(jié)點承載設(shè)備連接,而又不會直接增加核心節(jié)點寫操作的延時,從而達到擴展集群規(guī)模的效果。
Mria 單復制拓撲架構(gòu)
但是Mria這種架構(gòu)實現(xiàn)也不是銀彈,雖然它可以解決全網(wǎng)狀復制帶來的數(shù)據(jù)同步問題,但是依然無法很好地處理所有節(jié)點都要承載集群全量數(shù)據(jù)的問題。
另外需要特別注意的是,為了提高Mria架構(gòu)的復制效率,EMQX官方在Erlang/OTP實現(xiàn)的基礎(chǔ)上引入了一個叫做post-commit鉤子的實現(xiàn)。如果要應(yīng)用新的Mria架構(gòu),需要使用有此補丁的Erlang/OTP庫,否則集群會自動降級到Mnesia的實現(xiàn)模式。遺憾的是,到目前為止,該新特性并未合并到Erlang/OTP官方倉庫中,需要研發(fā)人員自己構(gòu)建帶有此補丁的依賴庫。
PR: mnesia: Add post-commit hook #5926
4.AMQ 2.0:基于角色的路由分發(fā)
AMQ是中國移動智慧家庭運營中心自研的基于開源EMQX實現(xiàn)的物聯(lián)網(wǎng)連接中間件。為了增加集群的擴展能力,我們在2.0版本中引入了Mria開源實現(xiàn)的新特性,用于解決集群節(jié)點復制的開銷問題。同時,為了解決所有節(jié)點需要承載集群全量數(shù)據(jù)的問題,我們設(shè)計了新的集群數(shù)據(jù)復制實現(xiàn)——連接分發(fā)引擎:一種基于節(jié)點角色進行訂閱/復制的路由分發(fā)機制。
路由數(shù)據(jù)是物聯(lián)網(wǎng)連接集群中的核心數(shù)據(jù),它存儲設(shè)備訂閱主題與集群節(jié)點的映射關(guān)系,在消息發(fā)布時根據(jù)消息主題信息查找所有匹配的節(jié)點,用于集群內(nèi)節(jié)點間的消息派發(fā)。在EMQX的實現(xiàn)中,路由數(shù)據(jù)存在于集群中的所有節(jié)點上。客戶端的主題訂閱數(shù)據(jù),則只保存在連接所在的節(jié)點上,用于節(jié)點內(nèi)部派發(fā)消息到客戶端。當客戶端連接到集群某個節(jié)點訂閱某個新的主題時,就會生成一條路由數(shù)據(jù),該數(shù)據(jù)最終會同步到集群所有節(jié)點上,每個節(jié)點都可以通過本地查詢找到任意主題對應(yīng)的訂閱節(jié)點列表。當客戶端發(fā)布消息時,連接所在節(jié)點會根據(jù)消息主題檢索路由數(shù)據(jù)得到所有訂閱節(jié)點的信息,然后將消息派發(fā)到這些節(jié)點上。
Mria實現(xiàn)的一個問題就在于,集群中很多節(jié)點復制了本身就不需要的路由數(shù)據(jù)。設(shè)想這樣一種場景:一個智能門鎖和一個智能臺燈分別連接到集群中的NodeA和NodeB節(jié)點上,并且分別訂閱了主題TopicA和TopicB。由于EMQX實現(xiàn)的特性,這兩個節(jié)點都會存儲一條包含TopicA和TopicB的路由數(shù)據(jù)。但由于門鎖和臺燈之間不會直接互相發(fā)布消息,對這兩個節(jié)點來說,他們都存儲了一條永遠也不會用到的路由數(shù)據(jù)。同理,當集群中接入的設(shè)備越來越多時,每個節(jié)點上都會存在大量無用的路由數(shù)據(jù)記錄。這不僅會增加服務(wù)器資源的投入,還會導致查詢性能的降低,另外在新節(jié)點加入集群時,還會導致數(shù)據(jù)復制時間的增加,降低節(jié)點的接入效率。
在AMQ 2.0實現(xiàn)的路由分發(fā)機制中,每個節(jié)點都有一個數(shù)據(jù)復制角色:DB(Database),SVC(Service)或者CONN(Connection)。其中只有少數(shù)的DB角色節(jié)點才擁有全量的集群數(shù)據(jù),在集群中承擔“數(shù)據(jù)中心”的角色。DB節(jié)點負責根據(jù)可配置的訂閱策略將路由數(shù)據(jù)分發(fā)給對應(yīng)的SVC或CONN節(jié)點。另外,SVC和CONN節(jié)點并不會簡單地復制DB節(jié)點的所有路由數(shù)據(jù),而是根據(jù)可配置的角色策略選擇性地復制自己所需要的數(shù)據(jù)。這樣,這些節(jié)點存儲的數(shù)據(jù)就是有限的,并不會隨著集群數(shù)據(jù)量的增加而增加,并且仍舊采取本地查詢的方式檢索數(shù)據(jù),不會影響消息派發(fā)時數(shù)據(jù)檢索的效率。此外,由于不需要同步集群全量數(shù)據(jù),每個SVC和CONN節(jié)點都可以做到快速接入、快速完成存量數(shù)據(jù)的復制。
AMQ基于角色的路由分發(fā)拓撲架構(gòu)
5.總結(jié)
無論是EMQX 4.x的Mnesia實現(xiàn),還是EMQX 5.x的Mria實現(xiàn),亦或是AMQ 2.0的路由分發(fā)實現(xiàn),目的都是一樣的:在確保數(shù)據(jù)讀寫效率的前提下,盡可能地擴展集群的規(guī)模。