技術基礎 | 改進版的Apache Cassandra客戶端請求路由

最近咱們在客戶端的驅動程序中引入了一些變動,這些變動會影響傳入的請求在Apache Cassandra集羣內的分發方式。html

 

新的默認負載均衡算法即將隨驅動程序推出,這些算法將有助於縮短長尾延遲,並提供更好的整體響應時間。nginx


 

 

01 Cassandra中數據分區和數據複製的方式算法

Cassandra根據分區鍵(partition key)的值將數據分配至節點。每一個分區鍵對應的分區有多個副本,從而確保可靠性和容錯能力。數據庫

 

複製策略決定了要把這些副本放置在哪些節點。整個集羣中的副本總數被稱爲「複製因子(replication factor)」。例如,「複製因子爲3」就表示在一個給定的數據中心或集羣中,每條數據有三個副本。服務器

 

對於一個給定的查詢請求,驅動程序會選擇一個副本節點做爲本次查詢的協調節點(coordinator),並負責知足一致性級別(consistency level)的要求。網絡

 寫入路徑示例:客戶端驅動程序選擇了節點A,該節點A是某個給定的分區鍵的副本節點,它會做爲本次請求的協調節點將數據發送到其餘副本節點(E和C)負載均衡

 

 

02 客戶端是如何路由的dom

DataStax Drivers(DataStax的驅動程序)控制傳入的查詢請求將如何在集羣中分發。藉助集羣的元數據,DataStax Drivers能夠知道哪些節點是某個給定分區鍵的副本節點。而後驅動程序會將查詢請求直接發送到其中的一個副本節點,以免額外的網絡躍點(network hop)。分佈式

 

當複製因子大於1時,驅動程序會把收到的請求在不一樣的副本間保持平衡。以往,驅動程序經常以隨機的方式,從副本節點集(replica set)中爲某個查詢選擇協調節點。微服務

 

 

03 隨機選擇

鑑於微服務應用程序一般包含多個服務實例,咱們必須將客戶端的驅動程序視爲一種分佈式的負載均衡器(n個客戶端將流量路由到m個節點)。隨機選擇是一種很好的分佈式負載均衡器的算法,由於它是無狀態的,不須要客戶端實例互相通訊以後生成統一的負載。

Image

多個客戶端將多個請求分發到同一些節點

 

隨機選擇讓請求負載被均勻地分配(從數量的角度),但這些負載的分配卻不是公平的——由於隨機選擇沒有考慮請求的大小或複雜性,也沒考慮在服務器節點的後臺可能正在處理的其餘任務。

 

 

03 隨機選擇的二次方(The Power of Two Random Choices)

在分佈式系統中,根據某個標識信號就肯定性地選擇某一個候選節點,這多是很危險的——由於具備相同邏輯的多個客戶端,可能會將同一服務器節點標識爲最佳候選節點,並將全部流量導向到該節點。這會致使負載峯值,並有級聯失效(cascading failure)的風險。所以,具備必定程度的隨機性的算法仍然是咱們所須要的。

 

經過泰勒·麥克穆倫(Tyler McMullen)演講,咱們首此接觸到「隨機選擇的二次方(The Power of Two Random Choice)」這個算法。在那以後,這個算法已經被諸多負載均衡軟件(諸如Netflix的ZuulNginxHaproxy之類的)實現,用於衆多的服務網格部署(service mesh deployment)中(好比Twitter)。

 

這個算法的邏輯很簡單:相比試着根據某個標識信號來選出那個絕對最佳的候選節點,「隨機選擇的二次方」這個算法會讓你隨機選擇兩個候選節點,而後再在這二者中選擇更好的那個,同時也避免了更糟的選擇。

 

咱們有幾種不一樣的方法來識別哪一個節點是最佳候選節點。過去,咱們公開了一種使用時延信息(LatencyAwarePolicy)做爲標識信號的方法。因爲咱們在路由數據庫請求時沒有考慮查詢的複雜性,所以聚合的節點時延信息並非服務器實例的當前健康狀態的最佳指標。

 

例如,某個查詢可能因爲須要整理合並大量數據而須要更長的時間,可是該服務器可能仍有大量的空閒資源來處理其餘查詢。另外,請求的響應時間是在收到響應以後才肯定的,但是這已經不能反映服務器的最新狀態了。

 

客戶端驅動實例會向節點發送請求,咱們決定用正在處理中的請求數來選擇最佳候選節點。簡而言之,咱們會隨機選擇兩個副本,而後選擇其中等待處理的請求較少的副本節點做爲協調節點。

 

 

04 監測陳舊隊列

藉助咱們對節點的行爲模式的瞭解,咱們發現了更多的改進空間。當節點正在進行壓實或垃圾回收(GC)時,它可能須要更長的時間來處理請求隊列中的項目。若是僅僅基於最短隊列的算法,在負載增長的狀況下,即便節點的請求隊列過期,客戶端驅動程序也可能會繼續向其路由流量。

 

例如,請考慮如下情形。

 

 系統中有等待處理的請求,用隊列來表示

 

請求源源不斷而來,應用程序中的客戶端驅動程序實例在不斷地平衡發送至三個副本節點請求。在某個時間點,節點C變得很忙(即一段時間不能處理請求)。其餘正常的副本節點將繼續處理請求,如圖所示。

 

與此同時,系統傳入的負載正在增長,此時健康的副本節點們就會收到更多的待處理請求。

 

 因爲負載增長,健康的節點收到的待處理請求的數量增長了 

 

在這種狀況下,咱們之前會將請求路由到那個沒法以正常速度處理請求的節點(即上圖中的節點C)。

 

爲了克服上述狀況,咱們引入了第二個指標:節點是否有待處理的請求(10+),而且在最近的幾百毫秒內還沒發回任何響應。這個指標並不做爲」隨機選擇的二次方「的標識信號,而是做爲對副本節點的健康情況進行的初步探測。

 

若是大多數副本節點是健康的,那麼不健康的副本節點將不會做爲初始的協調節點(它們仍會被歸入重試和投機性執行speculative execution的考慮範圍)。

 

爲何把探測副本節點的健康情況做爲第一步?由於若是有大量節點是不健康的,則可能意味着該指標不適用於當前的工做負載、查詢類別或預計的延遲時間。這樣,咱們確保不會根據一個在特定狀況下並不適用的標識信號作出錯誤的決定, 從而避免了讓狀況變得更糟 。

 

 

05 總結

咱們針對具備多個客戶端的一系列場景同時測試了這個新算法,經過CPU burns(stress-ng)和人工網絡延遲(tc)模擬了處於壓力下的節點,結果是該新算法的性能優於全部其餘方法。

 

做爲示例,下圖按百分位分佈展示了延遲數據,在一些節點被CPU burns的狀況下,對新算法和隨機選擇算法進行了比較。

具備較低尾部延遲的新算法(藍色)

 

新的負載平衡算法基於「隨機選擇的二次方」,再加上可以躲開忙碌節點的邏輯判斷,即將成爲DataStax Drivers新的默認設置。藉助簡單的邏輯判斷,這個新的算法繞開了最差的候選節點,同時保證了流量分配有必定程度的隨機性。於是它可以避免你們不但願看到的羊羣效應,大大下降了尾部延遲時間。

 

咱們將在全部的客戶端驅動程序中採用這種新算法。Java和Node.js驅動程序從4.4版開始已經默認使用此算法,其他驅動程序也將很快跟上。

相關文章
相關標籤/搜索