在IaaS(Infrastructure as a Service,即基礎設施即服務)軟件裏許多任務要順序的執行;例如,當一個起動虛擬機的任務正在運行時,一個結束些虛擬機的任務則必有等待以前的開始任務結束才行。另外一方面,一些任務以須要併發的同時運行;例如,在同一主機上20個建立虛擬機的任務能同時運行。同步和並行在一個分佈式系統中是很差控的而且經常須要一個同步軟件。針對這個挑戰,ZStack提供了一個基於隊列的無鎖架構,容許任務很容易的來控制它們的並行級別,從一個同步到N個並行都行。java
一個好的IasS軟件在任務的同步及並行上須要有精細的控制。大多數狀況下,任務之間有依賴關係須要以某一順序來執行;例如,一個刪除卷的任務不能被執行,若是另外一個在此捲上作快照備份的任務正在執行中。有時,任務要併發執行來提高性能;例如,在同一臺主機上十個建立虛擬機的任務同時執行一點問題也沒有。固然,沒有正常的控制,並行任務也會損壞系統;例如,1000個同時執行的建立虛擬機的任務雖不會使系統掛掉但至少致使系統有段時間沒有響應。這種同步開發問題在多線程環境是很複雜的,在分佈式環境就顯得更加複雜了。算法
教科書告訴咱們,鎖和信號量是同步和並行的答案;在分佈式系統中,處理同步和並行的最直接的想法是,使用某種分佈式的協調軟件,像 Apache ZooKeeper ,或者在 Redis之上構建的相似軟件。 分佈式協調軟件的使用概況,例如, ZooKeeper,像下面這樣:數據庫
問題是,對於鎖或信號量, 線程須要等待其它線程釋放它們正在使用的鎖或信號量。在ZStack 的伸縮性祕密(第一部分)異步架構(ZStack's Scalability Secrets Part 1: Asynchronous Architectue) 一文中,咱們解釋了,ZStack是一種異步軟件,線程不會因等待其它線程的完成而阻塞;所以,鎖和信號量不是可行的選項。同時,咱們也關心使用分佈式協調軟件的複雜性和拓展性,想象一下,一個滿載100,000個須要鎖的任務的系統,這既不容易,也不易拓展。編程
同步的vs. 同步化的:在 ZStack 的伸縮性祕密(第一部分)異步架構(ZStack's Scalability Secrets Part 1: Asynchronous Architecture)一文中, 咱們討論了 同步的 vs. 異步的,在本文中,咱們將會討論 同步的 vs. 並行的。「同步的」和「同步化的」有時候是可互換的使用,可是它們是不一樣的。在咱們的場景中,「同步的」是在討論,關於執行一個任務是否會阻塞線程的問題;「同步化的」是在討論,關於一個任務是否排它的執行的問題。若是一個任務在完成前,一直佔據一個線程的全部時間,這就是一個同步的任務;若是一個任務不能和其它任務在同一時間執行,這就是一個同步化的任務。api
使用一致性哈希算法,來保證同一個服務實例可以處理全部到達同一資源的消息,這就是無鎖架構的基礎。經過這種方法彙集到達某一節點的消息,能夠減小從分佈式系統到多線程環境的同步,並行化的複雜性(更多細節見ZStack的伸縮性祕密(第二部分):無狀態服務)。網絡
注意:在深刻了解細節以前,請注意,咱們即將要談論的隊列,和在 ZStack 的伸縮性祕密(第二部分)無狀態服務(ZStack's Scalability Secrets Part 2: Stateless Services)一文中提到的RabbitMQ消息隊列,沒有任何關聯。消息隊列是RabbitMQ的術語;ZStack的隊列則是內部數據結構。數據結構
在ZStack中的任務是由消息驅動的,聚合消息讓相關的任務能夠在一樣的節點執行,減輕了經典的線程池併發編程的壓力。爲了不鎖競爭,ZStack使用工做隊列替代鎖和信號量。同步化的任務能夠一個接一個的執行,它們由基於內存的工做隊列維護:多線程
注意:工做隊列能夠同時執行同步化的和並行的任務。若是並行級別爲1,那麼隊列就是同步化的;若是並行級別大於1,那麼隊列是並行的;若是並行級別爲0,那麼隊列就是無限並行的。架構
在Zstack中有兩種工做隊列;一種是同步隊列,任務返回結果才認定爲結束(一般使用Java Runnable接口來實現):併發
thdf.syncSubmit(new SyncTask<Object>() { @Override public String getSyncSignature() { return "api.worker"; } @Override public int getSyncLevel() { return apiWorkerNum; } @Override public String getName() { return "api.worker"; } @Override public Object call() throws Exception { if (msg.getClass() == APIIsReadyToGoMsg.class) { handle((APIIsReadyToGoMsg) msg); } else { try { dispatchMessage((APIMessage) msg); } catch (Throwable t) { bus.logExceptionWithMessageDump(msg, t); bus.replyErrorByMessageType(msg, errf.throwableToInternalError(t)); } } /* When method call() returns, the next task will be proceeded immediately */ return null; } });
強調: 在同步隊列中,工做線程繼續讀取下個Runnable,只要前一個Runnable.run()方法返回結果,而且直接隊列爲空了才返回線程池。由於任務在執行時會取得工做線程,隊列是同步的.
另外一種是異常工做隊列,當它發出一個完成通知才認爲結束:
thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("start-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { startVm(msg, chain); /* the next task will be proceeded only after startVm() method calls chain.next() */ } });
強調: 在異步隊列中,ChainTask.run(SyncTaskChain chain) 方法可能在作一些異步 操做後當即返回;例如,發送消息和一個註冊的回調函數.在run()方法返回值後,工做線程回到線程池中;可是,以前的任務可能還沒完成,沒有任務可以被處理,直到以前的任務發出一個通知(如調用SyncTaskChain.next())。由於任務不會阻塞工做線程等待其完成,隊列是異步的。
基於內存的工做隊列簡單快速,它知足了在單一管理節點99%的同步和並行的須要; 然而,與建立資源相關的任務,可能須要在不一樣管理節點之間作同步。一致性哈希環基於資源UUID來工做,若是資源未被建立,它將沒法得知哪一個節點應該處理這個建立的工做。在大多數狀況下,若是要建立的資源不依賴於其它未完成的任務,ZStack會選擇,此建立任務的提交者所在的本地節點,來完成這個工做。不幸的是,這些不間斷的任務依賴於名爲虛擬路由VM的特殊資源; 例如,若是使用一樣的L3網絡的多個用戶VM,由運行於不一樣管理節點的任務建立而成,同時在L3網絡上並沒有虛擬路由VM,那麼建立虛擬路由VM的任務則可能由多個管理節點提交。在這種狀況下,因爲存在分佈式同步的問題,ZStack使用基於數據庫的做業隊列,這樣來自不一樣管理節點的任務就能夠實現全局同步。
數據庫做業隊列只有異步的形式;也就是說,只有前一個任務發出一個完成通知後,下一個任務才能執行。
注意: 因爲任務存儲在數據庫之中,因此數據庫做業隊列的速度比較慢;幸運的是,只有建立虛擬路由VM的任務須要它。
雖然基於無鎖架構的隊列能夠處理99.99%的時間同步,可是有一個爭用條件從一致的散列算法中產生:一個新加入的節點將分擔一部分相鄰節點的工做量,這就是一致的散列環的擴張的結果。
在這個例子中,在三個節點加入後,之前的目標定位從節點2轉到了節點3;在此期間,若是對於資源的一箇舊任務依舊工做在節點2上,可是對於相同資源的任務提交到節點3,這就會形成爭用狀態。然而,這種情況並非你想像中的那麼壞。首先,衝突任務不多地存在規則的系統中,好比,一個健全的 UI 不容許你阻止一個正在運行的 VM。而後,每個 ZStack 資源都有狀態,一個開始就處於問題狀態的任務會出現錯誤;好比,若是一個 VM 是中止狀態,一個附加任務量的任務就會馬上出錯。第三,代理--大多數任務的傳送地,有額外的附加機制;好比,虛擬路由器代理會同步全部的修改 DHCP 配置文件的請求,即便咱們已經有了虛擬路由器在管理節點端的工做隊列。最後,提早規劃你的操做是持續管理雲的關鍵;操做團隊能夠在推出雲以前快速產生足夠的管理節點;若是他們真的須要動態添加一個新的節點,這樣作的時候,工做量仍是比較小的。
在這篇文章裏,展現了創建在基於內存工做隊列和基於數據庫的無鎖結構。沒有涉及複雜的分佈式協做軟件,ZStack 儘量地在爭用條件下的屏蔽任務中配合提高性能。