每一個ZStack服務都是無狀態的,讓服務高可用以及橫向拓展(scale out)能夠很簡單,只須要啓動剩餘的服務實例,而後進行負載均衡便可。此外,ZStack將全部的服務打包到名爲管理節點(management node)的單個進程,它讓部署和管理變得超級簡單。java
在ZStack的伸縮性祕密武器——1、異步架構(ZStack's Scalability Secrets Part 1: Asynchronous Architecture)一文中, 咱們已經詳細解釋了異步架構,它讓單個ZStack管理節點能勝任大多數的雲端工做負載。然而,當用戶但願創建高可用的生產環境,或者處理超級大的併發工做負載的時候,一個管理節點是不夠的。解決方案是,構建一個分佈式的系統,這樣工做負載能夠延展到每個單一管理節點。這種增長新節點來拓展整個系統的容量的方式稱爲 橫向拓展(scale out).node
設計一個分佈式的系統並不容易。一個分佈式的系統,特別是一個有狀態的系統,必須處理一致性,可用性,以及分區容忍性(請查看 CAP理論(CAP theorem)),全部這些都很複雜。相反,一個無狀態的分佈式系統,在某種程度上擺脫了這種複雜性。首先,由於在節點之間無需狀態共享,系統天然保持了一致性;其次,因爲節點之間是相似的,當系統遇到一個分區問題一般也是OK的。鑑於此,一個分佈式的系統,一般更傾向於保持無狀態而不是有狀態。可是,設計一個無狀態的分佈式系統也是很困難的,同時,經常比設計有狀態的分佈式系統更加困難。提高了消息總線(message bus)和數據庫優點的ZStack,構建了一個包含了無狀態服務的無狀態分佈式系統。算法
因爲無狀態服務是保證整個系統無狀態的根基,在討論它是什麼以前,讓咱們先了解下什麼是「狀態」。在ZStack裏面,資源,如主機,虛擬機,鏡像,以及用戶,都是由單個服務管理的;當系統中存在多餘一個服務實例的時候,資源會被劃分爲不一樣的實例。例如,假若有10,000個虛擬機和兩個虛擬機服務實例,理想的狀況下,每一個實例將會管理5000個虛擬機:數據庫
因爲存在兩個服務實例,在向虛擬機發送請求以前,請求者必須知道哪個實例正在管理虛擬機;不然,它將沒法知道將請求發往何處。像 」哪一個服務實例正在管理什麼資源「 的認知,正是咱們正在談論的狀態。若是服務是有狀態的,狀態也就顯如今服務之中。請求者須要在某個地方諮詢這些狀態。當服務實例的數目發生變化的時候,服務須要交換狀態,例如,當一個新的服務實例加入,或者當前的服務實例脫離的時候。api
狀態交換是讓人擔心的,它很容易致使錯誤,經常會限制系統的可拓展性。爲了讓系統更可靠,同時更易於橫向拓展,理想的方式是,經過彼此分隔狀態來讓服務保持無狀態(查看 服務無狀態原則(Service Statelessness Principle)。 有了無狀態的服務,請求者再也不須要詢問何處發送請求;當新的服務實例加入,或者舊的服務實例脫離的時候,服務也再也不須要交換狀態。架構
注意:在接下來的內容中,爲了簡單起見,術語「服務」和「服務實例」交換着使用。併發
服務,經過中央消息總線(central message bus)--RabbitMQ,來彼此通信,它們是ZStack中的「第一等公民」。app
不像一般的微服務架構,其每一個服務都在單獨的進程或單獨的機器上運行,ZStack將全部的服務打包到一個名爲管理節點的單一進程。對於這個號稱 進程中的微服務(in-process microservices)架構,咱們有充分的理由,你能夠參看進程中的微服務架構(The In-Process Microservices Architecture)。負載均衡
一個管理節點是一個完整功能的ZStack軟件。因爲包含了無狀態服務,管理節點沒有共享狀態,可是有心跳記錄,以及一致性哈希算法環(consistent hashing ring)--接下來咱們將詳細介紹。 心跳用來監控管理節點的「健康」(譯者注:即此管理節點是否存活,是否正常運轉),只要一個管理節點在給定的間隔內中止更新心跳,其它的管理節點將會驅除它,同時開始接管它所管理的資源。less
實現無狀態服務的核心技術,特別是對於ZStack的業務邏輯,就是一致性哈希算法(consistent hashing algorithm)。在啓動的時候,每一個管理節點都會被分配一個 版本4UUID(version 4 UUID)(管理節點UUID),它會和服務名一塊兒,在消息總線上註冊一個服務隊列。例如,管理節點可能註冊以下所示的服務隊列:
zstack.message.ansible.3694776ab31a45709259254a018913ca zstack.message.api.portal zstack.message.applianceVm.3694776ab31a45709259254a018913ca zstack.message.cloudbus.3694776ab31a45709259254a018913ca zstack.message.cluster.3694776ab31a45709259254a018913ca zstack.message.configuration.3694776ab31a45709259254a018913ca zstack.message.console.3694776ab31a45709259254a018913ca zstack.message.eip.3694776ab31a45709259254a018913ca zstack.message.globalConfig.3694776ab31a45709259254a018913ca zstack.message.host.3694776ab31a45709259254a018913ca zstack.message.host.allocator.3694776ab31a45709259254a018913ca zstack.message.identity.3694776ab31a45709259254a018913ca zstack.message.image.3694776ab31a45709259254a018913ca zstack.message.managementNode.3694776ab31a45709259254a018913ca zstack.message.network.l2.3694776ab31a45709259254a018913ca zstack.message.network.l2.vlan.3694776ab31a45709259254a018913ca zstack.message.network.l3.3694776ab31a45709259254a018913ca zstack.message.network.service.3694776ab31a45709259254a018913ca zstack.message.portForwarding.3694776ab31a45709259254a018913ca zstack.message.query.3694776ab31a45709259254a018913ca zstack.message.securityGroup.3694776ab31a45709259254a018913ca zstack.message.snapshot.volume.3694776ab31a45709259254a018913ca zstack.message.storage.backup.3694776ab31a45709259254a018913ca
說明:你應該注意到,全部隊列都以一樣的UUID結尾,那是管理節點的UUID。
資源,如主機,容量,虛擬機,也是經過UUID來標識的。消息,經常和資源相關聯,是在服務間傳遞的。在發送消息以前,發送者必須選擇基於資源的UUID的接收者服務,這時,一致性哈希算法就開始登場了。
一致性哈希(Consistent hashing)是一種特別的哈希,當哈希表調整大小的時候,就會用到一致性哈希,其中只有一部分鍵(key)須要從新映射。關於一致性哈希的更多內容,更詳細的請參閱 這裏。在ZStack之中,管理節點由一致性哈希環組成,以下所示:
每一個管理節點都維護一份一致性哈希環的拷貝,這個環包含了系統中全部管理節點的UUID。當管理節點加入或者脫離的時候,生命週期事件(lifecycle event)就會經過消息總線廣播到其它節點,這樣使得這些節點擴展或者收縮環,以呈現當前系統的狀態。當發送消息的時候,發送者服務將使用資源的UUID,經過哈希的方式得出目標管理節點的UUID。例如,發送VM的UUID爲932763162d054c04adaab6ab498c9139的StartVmInstanceMsg,僞代碼以下:
msg = new StartVmInstanceMsg(); destinationManagementNodeUUID = consistent_hashing_algorithm("932763162d054c04adaab6ab498c9139"); msg.setServiceId("vmInstance." + destinationManagementNodeUUID); cloudBus.send(msg)
若是有一個穩定的環,那麼包含一樣資源UUID的消息就老是會路由到某個管理節點上一樣的服務,這就是ZStack無鎖架構的基礎(參閱 ZStack的伸縮性祕密(第三部分):無鎖架構(Stack's Scalability Secrets Part 3: Lock-free Architecture)。
當一致性哈希環收縮或釋放的時候,因爲一致性哈希的特性,只有少數節點受到輕微影響。
因爲一致性哈希環,發送者無需知道哪個服務實例即將處理消息;取而代之的是,這將會被處理掉。服務無需維護和交換,關於它們正在管理什麼資源的信息;它們所須要作的就是,處理即將到來的消息,由於環可以保證消息找到正確的服務實例。這就是服務如何變得超級簡單和保持無狀態的。
除包含資源UUID的消息以外(如 StartVmInstanceMsg, DownloadImageMsg),也有一類無資源UUID的消息,一般是建立型的消息(如 CreateVolumeMsg)和非資源消息(如 AllocateHostMsg)--它們不會操控單獨的資源。考慮到這些消息能夠發送到任意管理節點的服務,它們可能被故意發送到本地的管理節點,因爲發送者和接收者在一樣的節點,當發送者發送消息的時候,接收者固然也是可達的。
對 API 消息(例如:APIStartVmInstanceMsg)來講,有一個特殊的處理,它們老是發送一個衆所周知的服務 ID api.portal 。在消息總線上,一個全局的隊列被叫作 zstack.message.api.portal ,它被全部的管理節點 API 服務所共享,消息服務 ID api.portal 將會自動對其中的一個API服務作負載均衡,這個服務還會路由轉發消息到正確的目的地,並使用了一致性哈希環(consistent hashing ring)。經過這種作法,ZStack 隱藏了來自 API 客戶端消息路由轉發的細節,並簡化了寫一個ZStack API 客戶端的工做。
msg = new APICreateVmInstanceMsg() msg.setServiceId("api.portal") cloudBus.send(msg)
在這篇文章中,咱們證實了Zstack 構建伸縮性的分佈式系統。由於管理節點共享的信息比較少,很容易創建一個大的集羣,可能有幾十個甚至幾百個管理節點。然而實際上,在私有云方面,兩個管理節點能夠有很好的擴展性;在公共雲方面,管理員能根據工做量建立一個管理節點。依靠異步架構和無狀態的服務,Zstack可以處理大量的併發任務,現有的IaaS軟件則不能處理。