聊聊高可用的 11 個關鍵技巧

大家好,我是 Tom 哥

大型互聯網架構設計,講究一個四件套組合拳玩法,高併發高性能高可用高擴展

如果能掌握這四個方面,應付大廠面試以及日常工作中的架構方案設計基本不是什麼難題。

今天,Tom 哥就帶大家學習下高可用都有哪些設計技巧?

一、系統拆分

有句古話 "牽一髮而動全身"。

面對一個龐然大物,如果沒有一個合理的分工分層。任何一個小小失誤都會被無限放大,釀成巨大災難。

萬物相通,回到我們的軟件架構。

早前的系統都是單體系統,比如電商業務,會員、商品、訂單、物流、營銷等模塊都堆積在一個系統。每到節假日搞個大促活動,系統擴容時,一擴全擴,一掛全掛。只要一個接口出了問題,整個系統都不可用。

“雞蛋不能放在一個籃子裏”,這種連帶風險換誰都承受不起。

因此,系統拆分 成了更多人的選擇。

慢慢的就有了我們現在看到的微服務架構,將一個複雜的業務域按 DDD 的思想拆分成若干子系統,每個子系統負責專屬的業務功能,做好垂直化建設,各個子系統之間做好邊界隔離,降低風險蔓延。

二、解耦

軟件開發有個重要原則 “高內聚、低耦合”。

小到接口抽象MVC 分層,大到 SOLID 原則23種設計模式。核心都是降低不同模塊間的耦合度,避免一處錯誤改動影響到整個系統。

就以開閉原則爲例,對擴展是開放的,對修改是關閉的。隨着業務功能迭代,如何做到每次改動不對原來的舊代碼產生影響。

Spring 框架給我們提供了一個很好的思路,裏面有個重要設計 AOP ,全稱(Aspect Oriented Programming),面向切面編程。

核心就是採用動態代理技術,通過對字節碼進行增強,在方法調用的時候進行攔截,以便於在方法調用前後,增加我們需要的額外處理邏輯。

當然還有一個重要思路就是事件機制,通過發佈訂閱模式,新增的需求,只需要訂閱對應的事件通知,針對性消費即可。不會對原來的代碼侵入性修改,是不是會好很多。

三、異步

同步指一個進程在執行請求的時候,若該請求需要一段時間才能返回信息,那麼這個進程將會一直等待下去,直到收到返回信息才繼續執行下去。

效率會大大降低,聰明的人想到了 異步 方式。

如果是非實時響應的動作可以採用異步來完成,線程不需要一直等待,而是繼續執行後面的邏輯。

如:線程池(ThreadPoolExecutor)、消息隊列 等都是這個原理

比如一個用戶在淘寶下了一筆購物訂單,關心的是訂單是否創建成功,能否進行後續的付款流程

至於其他業務動作,如短信通知、郵件通知、生成訂單快照、創建超時任務記錄,這些非核心動作用戶並不是特別關心。

我們可以採用消息隊列的發佈/訂閱 機制,數據庫插入訂單記錄後,發佈一條消息到 MQ,然後就可以告知用戶下單成功。

其他事情,由不同的 Task 任務訂閱消息異步處理,彼此間互不干擾。

四、重試

重試主要是體現在遠程的 RPC 調用,受 網絡抖動線程資源阻塞 等因素影響,請求無法及時響應。

爲了提升用戶體驗,調用方可以通過 重試 方式再次發送請求,嘗試獲取結果。比過:瀏覽器的 F5 刷新機制就是類似道理。

接口重試是一把雙刃劍,雖然客戶端收到了響應超時結果,但是我們無法確定,服務端是否已經執行完成。如果盲目地重試,可能會帶來嚴重後果。比如:銀行轉賬。

重試通常跟冪等組合使用,如果一個接口支持了 冪等,那你就可以隨便重試

關於的 冪等 的解決方案

五、補償

我們知道不是所有的請求都能收到成功響應。除了上面的 重試 機制外,我們還可以採用補償玩法,實現數據最終一致性

業務補償根據處理的方向分爲兩部分:

注意:補償操作有個重要前提,業務能接受短時間內的數據不一致。

補償有很多的實現方式:

1、本地建表方式,存儲相關數據,然後通過定時任務掃描提取,並藉助反射機制觸發執行

2、也可以採用簡單的消息中間件,構建業務消息體,由下游的的消費任務執行。如果失敗,可以藉助 MQ 的重試機制,多次重試

六、備份

任何服務器都有宕機的可能性,一旦存儲了數據,帶上狀態,如果發生故障,數據丟失,後果是我們無法承受的。

所以,容災備份也就變成了互聯網的基本能力。

那如何備份,不同的框架有不用的玩法。我們以 Redis 爲例:

Redis 藉助 RDBAOF 來實現兩臺服務器間的數據同步

一旦主節點掛了怎麼辦?

這裏引入哨兵機制。哨兵機制可以實現主從庫的自動切換,有效解決了故障轉移。整個過程分爲三個階段:監控、選主、通知。

除了 Redis 中間件外,其他常見的 MySQL、Kafka 消息中間件、HBase 、ES 等 ,凡是涉及到數據存儲的介質,都有備份機制,一旦主節點掛了,會啓用備份節點,保證數據不會丟失。

七、多活策略

雖然有了上面的備份策略,那是不是就萬事大吉呢?

在一些極端情況,如:機房斷電、機房火災、地震、山洪等不可抗力因素,所有的服務器都可能出現故障,無法對外提供服務,導致整體業務癱瘓。

爲了降低風險,保證服務的 24 小時可用性,我們會採用 多活策略

常見的多活方案有,同城雙活兩地三中心三地五中心異地雙活異地多活

不同的方案技術要求、建設成本、運維成本也都不一樣。

多活的技術方案複雜,需要考慮的問題點也非常多,這裏只是拋磚引玉就不過多展開

八、隔離

隔離屬於物理層面的分割,將若干的系統低耦合設計,獨立部署,從物理上隔開。

每個子系統有自己獨立的代碼庫,獨立開發,獨立發佈。一旦出現故障,也不會相互干擾。當然如果不同子系統間有相互依賴,這種情況比較特殊,需要有默認值或者異常特殊處理,這屬於業務層面解決方案。

隔離屬於分佈式技術的衍生產物,我們最常見的微服務解決方案。

將一個大型的複雜系統拆分成若干個微服務系統,這些微服務子系統通常由不同的團隊開發、維護,獨立部署,服務之間通過 RPC 遠程調用。

隔離使得系統間邊界更加清晰,故障可以更加隔離開來,問題的發現與解決也更加快速,系統的可用性也更高。

九、限流

高併發系統,如果遇到流量洪峯,超過了當前系統的承載能力。我們要怎麼辦?

一種方案,照單全收,CPU、內存、Load 負載飈的很高,最後處理不過來,所有請求都超時無法正常響應。

另一種解決方案,“捨得,有舍有得”,多餘的流量我們直接丟棄。

限流定義:

限制到達系統的併發請求數量,保證系統能夠正常響應部分用戶請求,而對於超過限制的流量,則通過拒絕服務的方式保證整體系統的可用性。

根據作用範圍:限流分爲單機版限流、分佈式限流

1、單機版限流

主要藉助於本機內存來實現計數器,比如通過 AtomicLong#incrementAndGet(),但是要注意之前不用的 key 定期做清理,釋放內存。

純內存實現,無需和其他節點統計彙總,性能最高。但是優點也是缺點,無法做到全局統一化的限流。

2、分佈式限流

單機版限流僅能保護自身節點,但無法保護應用依賴的各種服務,並且在進行節點擴容、縮容時也無法準確控制整個服務的請求限制。而分佈式限流,以集羣爲維度,可以方便的控制這個集羣的請求限制,從而保護下游依賴的各種服務資源。

限流支持多個維度:

常見的限流算法:

十、熔斷

熔斷,其實是對調用鏈路中某個資源出現不穩定狀態時(如:調用超時或異常比例升高),對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導致級聯錯誤。

熔斷的主要方式是使用斷路器阻斷對故障服務器的調用

斷路器有三種狀態,關閉、打開、半打開。

狀態機:

1、關閉(Closed)狀態:在這個狀態下,請求都會被轉發給後端服務。同時會記錄請求失敗的次數,當請求失敗次數在一段時間超過一定次數就會進入打開狀態。

2、打開(Open)狀態:在這個狀態下,熔斷器會直接拒絕請求,返回錯誤,而不去調用後端服務。同時,會有一個定時器,時間到的時候會變成半打開狀態。目的是假設服務會在一段時間內恢復正常。

3、半打開(Half Open)狀態:在這個狀態下,熔斷器會嘗試把部分請求轉發給後端服務,目的是爲了探測後端服務是否恢復。如果請求失敗會進入打開狀態,成功情況下會進入關閉狀態,同時重置計數。

目前,市面流行的解決方案是阿里的開源框架 Sentinel,提供了 Dashboard 控制檯用於定義資源以及規則配置

十一、降級

降級是系統保護的一種重要手段。

正如 “好鋼用在刀刃上”,爲了使有限資源發揮最大價值,我們會臨時關閉一些非核心功能,減輕系統壓力,並將有限資源留給核心業務。

比如電商大促,業務在峯值時刻,系統抵擋不住全部的流量時,系統的負載、CPU 的使用率都超過了預警水位,可以對一些非核心的功能進行降級,降低系統壓力,比如把商品評價成交記錄等功能臨時關掉。棄車保帥,保證 創建訂單訂單支付 等核心功能的正常使用。

當然,不同業務、不同公司,處理方式也各不相同,需要結合實際場景,和業務方同學一塊討論,最後達成一個統一認可的降級方案。

總結下來:降級是通過暫時關閉某些非核心服務或者組件從而保護核心系統的可用性。

關於我:Tom 哥,前阿里 P7 技術專家,offer 收割機,參加多次淘寶雙 11 大促活動。歡迎關注,我會持續輸出更多經典原創文章,爲你晉級大廠助力

目前微信羣已開放,想進交流羣的小夥伴請添加 Tom 哥微信,暗號「進羣」,嘮嗑聊天, 技術交流,圍觀朋友圈,人生打怪不再寂寞

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MSaugX48y5iia02UVpqb_Q