【高併發、高性能、高可用】系統設計經驗
軟件開發通常會提到一個名詞 “三高”,即高併發、高性能、高可用。
具體的指標定義,如:高併發方面要求 QPS 大於 10 萬;高性能方面要求請求延遲小於 100 ms;高可用方面要高於 99.99%。
接下來,我們重點來介紹這 三高
我們使用 QPS(Queries Per Second,每秒查詢率)來衡量系統承載能力。架構策略有哪些?
1、負載均衡
正所謂雙拳難敵四手,高併發撐場面的首選方案就是集羣化部署,一臺服務器承載的 QPS 有限,多臺服務器疊加效果就不一樣了。
如何將流量轉發到服務器集羣,這裏面就要用到負載均衡,比如:LVS 和 Nginx。
常用的負載算法有輪詢法、隨機法、源地址哈希法、加權輪詢法、加權隨機法、最小連接數法等
業務實戰:對於千萬級流量的秒殺業務,一臺 LVS 扛不住流量洪峯,通常需要 10 臺左右,其上面用 DDNS(Dynamic DNS)做域名解析負載均衡。搭配高性能網卡,單臺 LVS 能夠提供百萬以上併發能力。
注意, LVS 負責網絡四層協議轉發,無法按 HTTP 協議中的請求路徑做負載均衡,所以還需要 Nginx
2、池化技術
複用單個連接無法承載高併發,如果每次請求都新建連接、關閉連接,考慮到 TCP 的三次握手、四次揮手,有時間開銷浪費。池化技術的核心是資源的 “預分配” 和“循環使用”,常用的池化技術有線程池、進程池、對象池、內存池、連接池、協程池。
連接池的幾個重要參數:最小連接數、空閒連接數、最大連接數
Linux 內核中是以進程爲單元來調度資源的,線程也是輕量級進程。所以說,進程、線程都是由內核來創建並調度。協程是由應用程序創建出來的任務執行單元,比如 Go 語言中的協程 “goroutine”。協程本身是運行在線程上,由應用程序自己調度,它是比線程更輕量的執行單元。
在 Go 語言中,一個協程初始內存空間是 2KB(Linux 下線程棧大小默認是 8MB),相比線程和進程來說要小很多。協程的創建和銷燬完全是在用戶態執行的,不涉及用戶態和內核態的切換。另外,協程完全由應用程序在用戶態下調用,不涉及內核態的上下文切換。協程切換時由於不需要處理線程狀態,需要保存的上下文也很少,速度很快。
Go 語言中協程池的實現方法有兩種:搶佔式和調度式。
-
搶佔式協程池,所有任務存放到一個共享的 channel 中,多個協程同時去消費 channel 中的任務,存在鎖競爭。
-
調度式協程池,每個協程都有自己的 channel,每個協程只消費自己的 channel。下發任務的時候,採用負載均衡算法選擇合適的協程來執行任務。比如選擇排隊中任務最少的協程,或者簡單輪詢。
3、流量漏斗
上面講的是正向方式提升系統QPS,我們也可以逆向思維,做減法,攔截非法請求,將核心能力留給正常業務!
互聯網高併發流量並不都是純淨的,也有很多惡意流量(比如黑客攻擊、惡意爬蟲、黃牛、秒殺器等),我們需要設計流量攔截器,將那些非法的、無資格的、優先級低的流量過濾掉,減輕系統的併發壓力。
攔截器分層:
- 網關和 WAF(Web Application Firewall,Web 應用防火牆)
採用封禁攻擊者來源 IP、拒絕帶有非法參數的請求、按來源 IP 限流、按用戶 ID 限流等方法
-
風控分析。藉助大數據能力分析訂單等歷史業務數據,對同 ip 多個賬號下單、或者下單後支付時間過快等行爲有效識別,並給賬號打標記,提供給業務團隊使用。
-
下游的每個 tomcat 實例應用本地內存緩存化,將一些庫存存儲在本地一份,做前置校驗。當然,爲了儘量保持數據的一致性,有定時任務,從 Redis 中定時拉取最新的庫存數據,並更新到本地內存緩存中。
高性能
性能直接影響用戶的感官體驗,訪問一個系統,如果超過 5 秒沒有響應,絕大數用戶會選擇離開。
那麼有哪些因素會影響系統的性能呢?
-
用戶網絡環境
-
請求 / 響應的數據包大小
-
業務系統 CPU、內存、磁盤等性能
-
業務鏈路的長度
-
下游系統的性能
-
算法實現是否高效
當然,隨着併發數的提升,系統壓力增大,平均請求延遲也會增大。
1、高性能緩存
對一些熱點數據每次都從 DB 中讀取,會給 DB 帶來較大的壓力,導致性能大幅下降。所以,我們需要用緩存來提升熱點數據的訪問性能,比如將活動信息數據在瀏覽器的緩存中保存一段時間。
緩存根據性能由高到低分爲:寄存器、L1 緩存、L2 緩存、L3 緩存、本地內存、分佈式緩存
上層的寄存器、L1 緩存、L2 緩存是位於 CPU 核內的高速緩存,訪問延遲通常在 10 納秒以下。L3 緩存是位於 CPU 核外部但在芯片內部的共享高速緩存,訪問延遲通常在十納秒左右。高速緩存具有成本高、容量小的特點,容量最大的 L3 緩存通常也只有幾十 MB。
本地內存是計算機內的主存儲器,相比 CPU 芯片內部的高速緩存,內存的成本要低很多,容量通常是 GB 級別,訪問延遲通常在幾十到幾百納秒。
內存和高速緩存都屬於掉電易失的存儲器,如果機器斷電了,這類存儲器中的數據就丟失了。
特別說明:在使用緩存時,要注意緩存穿透、緩存雪崩、緩存熱點問題、緩存數據一致性問題。當然爲了提升整體性能通常會採用多級緩存組合方案(瀏覽器緩存+服務端本地內存緩存+服務端網絡內存緩存)
2、日誌優化,避免 IO 瓶頸
當系統處理大量磁盤 IO 操作的時候,由於 CPU 和內存的速度遠高於磁盤,可能導致 CPU 耗費太多時間等待磁盤返回處理的結果。對於這部分 CPU 在 IO 上的開銷,我們稱爲 “iowait”。
在 IO 中斷過程中,如果此時有其他任務線程可調度,系統會直接調度其他線程,這樣 CPU 就相應顯示爲 Usr 或 Sys;但是如果此時系統較空閒,無其他任務可以調度,CPU 就會顯示爲 iowait(實際上與 idle 無本質區別)。
磁盤有個性能指標:IOPS,即每秒讀寫次數,性能較好的固態硬盤,IOPS 大概在 3 萬左右。對於秒殺系統,如果單節點 QPS 在 10 萬,每次請求產生 3 條日誌,那麼日誌的寫入 QPS 在 30W/s,磁盤根本扛不住。
Linux 有一種特殊的文件系統:tmpfs(臨時文件系統),它是一種基於內存的文件系統,由操作系統管理。當我們寫磁盤的時候實際是寫到內存中,當日志文件達到我們的設置閾值,操作系統會將日誌寫到磁盤中,並將 tmpfs 中的日誌文件刪除。
這種批量化、順序寫,大大提升了磁盤的吞吐性能!
高可用
高可用指標是指用來衡量一個系統可用性有多高。
-
MTBF(Mean Time Between Failure),系統可用時長
-
MTTR(Mean Time To Repair),系統從故障後到恢復正常所耗費的時間
-
SLA(Service-Level Agreement),服務等級協議,用於評估服務可用性等級。計算公式是
MTBF/(MTBF+MTTR)
一般我們所說的可用性高於 99.99%,是指 SLA 高於 99.99%。
技術架構,高可用有哪些策略?
-
多雲架構、異地多活、異地備份
-
主備切換,如 redis 緩存、mysql 數據庫,主備節點會實時數據同步、備份。如果主節點不可用,自動切換到備用節點
-
微服務,無狀態化架構,業務集羣化部署,有心跳檢測,能最短時間檢測到不可用的服務。
-
通過熔斷、限流,解決流量過載問題,提供過載保護
-
重視 web 安全,解決攻擊和 XSS 問題
1、主備切換,縮減故障時間
當系統出現故障時,首要任務不是立馬查找原因,考慮到故障的複雜樣,定位排查要花些時間,等問題修復好,SLA 也降了好幾個檔。有沒有更快的方式解決這個問題?那就是故障轉移。
當發現故障節點的時候,不是嘗試修復它,而是立即把它隔離,同時將流量轉移到正常節點上。這樣通過故障轉移,不僅減少了 MTTR 提升了 SLA,還爲修復故障節點贏得了足夠的時間。
主備切換大致分爲三步:
-
第一步故障自動偵測(Auto-detect),採用健康檢查、心跳等技術手段自動偵測故障節點;
-
第二步自動轉移(FailOver),當偵測到故障節點後,採用摘除流量、脫離集羣等方式隔離故障節點,將流量轉移到正常節點;
-
第三步自動恢復(FailBack),當故障節點恢復正常後,自動將其加入集羣中,確保集羣資源與故障前一致。
2、熔斷,提供過載保護
所謂過載保護,是指負載超過系統的承載能力時,系統會自動採取保護措施,確保自身不被壓垮。
熔斷就是在系統瀕臨崩潰的時候,立即中斷服務,從而保障系統穩定避免崩潰。它類似於電器中的 “保險絲”,當電流過大的時候,“保險絲” 會先被燒掉,斷開電流,以免電路過熱燒燬電器引起火災。
例子:熔斷觸發條件往往跟系統節點的承載能力和服務質量有關,比如 CPU 的使用率超過 90%,請求錯誤率超過 5%,請求延遲超過 500ms, 它們中的任意一個滿足條件就會出現熔斷。
3、限流,提供過載保護
限流的原理跟熔斷有點類似,都是通過判斷某個條件來確定是否執行某個策略。但是又有所區別,熔斷觸發過載保護,該節點會暫停服務,直到恢復。限流,則是隻處理自己能力範圍之內的請求,超量的請求會被限流。
限流算法主要有:計數器限流、滑動窗口限流、令牌桶限流、漏桶限流。網上的資料很多,這裏就不多贅述。
4、降級
比如電商大促,業務在峯值時刻,系統抵擋不住全部的流量時,系統的負載、CPU 的使用率都超過了預警水位,可以對一些非核心的功能進行降級,降低系統壓力,比如把商品評價
、成交記錄
等功能臨時關掉。棄車保帥
,保證 創建訂單、支付 等核心功能的正常使用。
當然不同業務、不同公司處理方式也各不相同,需要結合實際場景,和業務方一塊討論,最後達成一個統一認可的降級方案。
總結下來:降級是通過暫時關閉某些非核心服務或者組件從而保護核心系統的可用性。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/I_a-gq4UNaJINV8KHaPxkA