超實用極簡客戶端Failover方案
閱讀更多相關文章,請關注微信公眾號[碼洞]
在應用結構上有這樣一個業務場景,機房裡部署了多個物理資料庫的Proxy無狀態節點,業務端通過Proxy節點間接和存儲DB交互。Proxy支持了分庫分表的特性,管理下層多個物理DB,向上層提供單表抽象。為了支持高可用性,Proxy為多節點部署,業務端可以隨機挑選Proxy收發消息。
這裡我們討論業務端SDK的Failover實現方案。SDK需要管理指向多個Proxy連接,每個請求都需要隨機挑選某個Proxy連接進行收發消息。當Proxy都正常時,隨機演算法已經可以滿足負載均衡了。但是Proxy是可能會突然宕機的,掛了一個Proxy節點後,SDK需要快速摘除宕機的Proxy連接,將所有的請求都轉移到其它節點之上。當這個Proxy節點恢復後,又可以重新將這個節點放回Proxy列表中。
那這種快速的動態調整,SDK又該如何以最簡單的方法進行實現呢?一般的思路如下
- 使用計數機制,當請求出現錯誤時,比如在一定的時間窗口裡出現了N次錯誤,那就可以標記該Proxy已損壞,從Proxy正常列表中摘除掉該Proxy,同時在恢複列表中加入該Proxy
- 使用Retry機制,每隔一段時間對恢複列表中的Proxy進行重試,重試一旦正確,就立即將Proxy從恢複列表中轉移至正常列表
- 如果所有的Proxy都損壞了,那最後一個Proxy是不可以隨便摘的。如果直接摘掉了,會導致Retry窗口內服務不可用。即使Proxy快速恢復了,也需要等待Retry窗口的時間才可以檢測到。一般的做法是,如果所有的Proxy都壞掉了,那請求的隨機Proxy列表不再是正常Proxy列表,而是全體Proxy列表。
要對這種思路進行編碼實現有一定的複雜度。比如下圖是pylibmc的狀態轉移圖,看一眼知道複雜度非同一般。
為降低複雜性,我設計了一個非常簡單的方案,可以很好的解決Proxy Failover的問題,步驟如下
- 給每個Proxy設定一個初值,比如說1024,該值作為隨機權重使用
- 每次請求出現失敗一次,就將權值除以一個數,比如說2,數字越大,降權越快。
- 當權值降低到最小值,比如說1時,不再繼續降權。這樣可以保持壞掉的Proxy以一個極低的概率得到重試。
- 只要有任何一個成功的請求,就將權值恢復到初值。
- 當SDK通過權重隨機挑選了一個Proxy進行了一個失敗的請求時,將重新隨機挑選Proxy進行重試,記錄重試次數,直到成功為止或者重試到了一個最大次數上限,向上層拋出異常。
這種方案的優勢在於不需要劃分出正常列表和恢複列表,沒有複雜的狀態遷移,而且不需要設置額外定時器進行重試。當所有的節點都壞掉的情況下,所有的Proxy權重也還是一樣的。我嘗試用代碼實現了這個方案,用了非常簡潔的十幾行代碼就搞定了Failover問題。
當然這種方案也不是完美的,它的缺點體現在需要仔細控制權重參數,初始值/降權係數/最小值,特別是最小值,如果設置的太小,而SDK的QPS又太低的話,Proxy可能會長時間得不到恢復,不過這種情況也沒有關係,如果QPS太小,要那麼多的Proxy做負載均衡也是多餘的。
繼續閱讀更多相關文章,請關注知乎專欄[碼洞]
推薦閱讀: