如何構建高可用的系統(二): 消除單點與自動故障轉移

前一篇文章我們講到, 一切單點都是不可靠的, 如果系統中某個地方可能會出問題(就算概率很低),那麼它遲早會出問題。 也就是我們常說的Murphy’s Law。如果要提高系統的可用性, 那麼就必須儘可能消滅掉系統中所有的單點,並且在發生故障時,把流量自動轉移到運行正常的節點。

在這篇文章中, 我會以互聯網產品後端常見的架構爲例, 講述如何達到這個目標。

1.常見系統架構

在這裏插入圖片描述

互聯網後端應用的常見架構可以用上圖來簡單描述(簡化了單機房內其他系統、多機房等):

  1. 在App或瀏覽器端, 通過DNS或自己的路由機制, 把域名解析爲IP, 從而進行網絡通信
  2. 後端系統的最外層, 通常是負載均衡器, 把流量分攤到下游的API (當然, LB內部也可以分更多層)
  3. API層處理業務邏輯,同時也會依賴下游服務。比如處理創建用戶的請求, API層可能需要做輸入內容的校驗,然後調用下游用戶服務來完成創建工作
  4. 服務層往往有自己的存儲系統, 涉及到數據庫、緩存, 同時也會利用MQ與其他系統進行解耦通信(當然API層可能也有)
  5. 與下游進行通信前,都需要通過服務發現機制,獲取到下游的具體實例信息, 方可進行通信

2.消除單點與自動故障轉移

2.1 DNS/自研路由機制

由於DNS系統一般不是我們自己維護, 所以這裏不討論DNS系統的高可用, 而是討論作爲它的用戶,我們可以做哪些事情來避免它成爲我們系統中的單點。

大家都知道, 我們依賴DNS來把域名解析爲後端系統入口的IP, 如果我們沒有其他的方案來兜底, 那DNS在我們的系統中就是個單點, 而且是致命單點: 假如DNS掛了, 那麼用戶就懵逼了,我們也懵逼了。

因此我們需要在APP端有自己的一套路由方案來做兜底: 比如我們自己搞套後端IP獲取系統, 定時獲取並把結果保存在端上, 這樣在DNS掛掉時, 端上可以用這些IP做兜底。

2.2 機房級單點

對於自然災害、機房斷電等問題, 即使單機房內的系統再高可用, 也是無法解決的。 因此往往需要引入多個機房來解決機房粒度的單點問題, 在機房出現重大故障時, 通過切換客戶端的訪問策略,把流量達到別的機房。

關於多機房架構設計,不是這篇文章所討論的重點, 感興趣的朋友可以閱讀我寫的另一個系列: 全球異地多活架構設計

2.3 LB層

關於負載均衡, 我之前寫過一篇總結類文章, 對這塊不瞭解的朋友可以先簡單看一下:關於負載均衡的一些總結

常見的提高LB層可用性的方法是使用Keepalived,可以通過一篇簡單的教程瞭解keepalived的作用: https://my.oschina.net/feinik/blog/1590941

2.4 API/Service層

API和Service層的高可用相對而言就簡單很多了。

我們可以起多個服務實例,然後把自己的ip、port註冊到服務註冊中心,上游通過服務發現機制, 獲取到這些地址並進行通信。一旦某個實例掛了, 服務註冊中心把對應的地址摘掉,上游訪問別的實例即可。

不過這裏需要注意的是, API、Service都應該是無狀態的。

2.5 存儲層

這裏以Mysql爲例,講講存儲層的策略。

大多數場景下, 我們至少都會選擇物理分片+一主多從的方式來提高可用性:

  1. 假如某個庫有16個物理分片, 當其中的一個分片宕機時, 可用性只降低了1/16
  2. 通過一主多從的方式, 當主庫宕機時, 起碼讀操作是不受影響的

如果我們希望在主庫宕機時也儘可能少的影響業務, 那就需要引入額外的高可用機制。 對於Mysql而言, 比較成熟的方案是MHA,它在主庫宕機時, 會從slave中選出新的主庫,並且使其他的Slave連接新的Master進行復制。
如果系統中引入了數據庫代理層(比如Mycat、Kingshard等), 還需要在主從關係發生變化時,動態的變更proxy中的配置。

當然, 除此之外, 我們還可以把數據冗餘到多個存儲中, 進一步提高可用性。 比如Mysql的數據實時導入hbase/mongodb(通過binlog), 當mysql集羣整個都掛掉時, 上游服務降級到讀hbase。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章