談談高併發系統的一些解決方案

xiaoxi666發表於2022-04-05

本文結合專案經驗,整理一份大綱,供參考。

常用指標

  • RT(Response Time):響應時間。可能會衍生出 TP999、TP99、TP95、TP90等指標。一般在幾毫秒到幾百毫秒之間。

  • QPS(Query Per Second):每秒查詢量。這是我們最常說的一個指標了。視業務複雜度不同而不同,輕量級的可能單機上萬,重量級的可能就幾百,這是主要依靠水平擴容來解決。

  • TPS(Transaction per second):每秒事務量。主要衡量資料庫效能,一般比 QPS 低 1~3 個數量級。

  • 吞吐量(Throughput):單位時間內處理的請求數量。該指標概念比較寬泛:

    • 從業務角度看,吞吐量可以用QPS、TPS等單位來衡量。

    • 從網路角度看,吞吐量可以用bps來衡量。一般用於排查網路抖動等問題,尤其是弱網環境下的RPC請求會更加關注。

  • PV(Page View):頁面瀏覽量。一般用於統計頁面訪問頻次,每次重新整理頁面即被計算一次。高併發系統的單日 PV 基本上會超過千萬。

  • UV(Unique Visitor):獨立訪客量。用於統計單位時間內頁面訪問的使用者數,同一個使用者多次重新整理頁面只會被算做一次。高併發系統的單日 UV 基本上會過萬。

 

提升系統效能的兩個維度

  • 垂直擴充套件:提升單機效能。包括硬體配置和軟體編寫方式兩種維度。

  • 水平擴充套件:叢集整體效能。無狀態服務加機器可解決;有狀態服務還要額外考慮狀態儲存及遷移。實踐中儘量做成無狀態服務。

  

災備管理

多區域部署機房,通常存在冷備、熱備、雙活等幾種形式。主要用於流量分攤和故障轉移。

 

故障處理

  • Failover 失敗自動切換:當出現失敗,重試其它伺服器,通常用於讀操作。一般核心服務會使用該策略。

  • Failfast 快速失敗:只發起一次呼叫,失敗立即報錯,通常用於非冪等性的讀寫操作。多數場景均有重試機制。

  • Failsafe 失敗安全:出現異常時,直接忽略。通常用於寫入日誌等操作。

  • Failback 失敗自動恢復:後臺記錄失敗請求,定時重發。通常用於訊息通知等操作。

  • Forking 並行呼叫多個服務方:其中一個成功即可返回,通常用於實時性要求較高的讀操作。

  • Broadcast廣播呼叫:所有提供方逐個呼叫,任意一臺報錯則報錯。主要用於RPC框架註冊節點使用(更新提供方本地狀態),應用型服務基本不會使用。

  

負載均衡

  • 監控機器效能,並配置機器的權重(靜態或動態)。

    • 處理能力越強,分到的流量越多。

    • 某臺機器故障時自動摘除流量。

    • 服務剛啟動時的小流量預熱,防止瞬間高流量把機器打崩。主要是各類中介軟體資源初始化可能會很耗時,導致請求響應慢,此時如果大量流量湧入會導致 Cpu Load急劇升高,甚至可能打崩。

  • 自動擴縮容。業務高峰時段自動擴容,低峰時段自動縮容,節約成本。

 

區分服務等級

  • 核心服務:一般都存在Backup,出錯時自動切換,同時觸發中高階別告警。

  • 非核心服務:出錯時可執行(手動或自動)降級甚至熔斷,同時觸發中低階別告警。

  

使用快取

主要針對不易變化的資料,可能是多級快取,可能橫跨客戶端和多個服務端。

  • 本地快取。如 ConcurrentMap、Guava Cache、Caffeine。

  • 分散式快取。如 Redis、GemFire/Geode。

  

非同步操作

  • 架構層面:

    • 如使用MQ訊息佇列進行削峰、解耦處理,日誌處理也用得比較多。

    • 若追求 Cpu 的穩定性,可使用 Spring WebFlux 等全鏈路非同步化技術,需要上下游服務都改造,才能有顯著效果。

  • 程式碼層面:如 Java 中的 Future 機制(常用 CompletableFuture),同時發起多個微服務的呼叫,隔一段時間後統一 get 結果。

  

批量執行

  • 框架層面:可參考 Hystrix 的請求合併機制 HystrixCollapser。

  • 程式碼層面:服務介面批量呼叫資料,拿到批量結果後再分派結果。

  

池化技術

  • 執行緒池:常用如 ThreadPoolExecutor。

  • 連線池:常用如 HikariCP、Druid、c3p0、DBCP。

  • 物件池:常用如 Apache Commons Pool2。另外,如 Integer 等包裝類針對(-127~128)的物件快取,其實也是一種物件池的體現。

  

限流處理

  • 服務入口:監控近實時統計QPS,達到閾值時拒絕請求。

  • 常見的幾種限流框架:

    • 單機版(JDK自帶的鎖、訊號量、Guava Limiter)

    • 分散式(基於 Redis 的 redis-cell 模組和 Redisson、重量級的 Sentinel,以及老牌框架Hystrix)

  • 常見的幾種限流演算法:

    • 計數器法。存在臨界流量問題,基本不會使用。

    • 滑動視窗。時間片劃分精度不好控制,基本不會使用。

    • 漏桶演算法。難以應對突發流量,使用較少。

    • 令牌桶演算法。常用。

  • 常見的執行緒池拒絕策略:

    • CallerRunsPolicy 由呼叫者執行。

    • AbortPolicy 拋棄並拋異常。這是預設策略,也是最常用的策略,可以讓應用層快速發現失敗,進而介入處理。

    • DiscardPolicy 靜靜地拋棄,應用層無法感知到。

    • DiscardOldestPolicy 拋棄最老的請求。

  

防刷分流

搭建兩套服務叢集,將存在爬蟲標記(依賴於專業的爬蟲識別演算法)的流量分流到另一套叢集,甚至可以返回假資料,做蜜罐處理。

 

靜態資源分發

主要依賴 CDN 技術進行資源的就近部署,可提前預熱。常見如html、js、css、image等資源。

 

資料庫併發

  • 單機:MVCC、事務隔離、做好索引優化。

  • 叢集:分庫分表、讀寫分離。

  • 結合其他中介軟體:如簡單的查詢、統計,或者文字搜尋等場景,可使用 ElasticSearch,必要時進行二級檢索( ElasticSearch 檢索出 id,再到 SQL 中查詢)。

 

壓力測試 /效能測試

  • Apache JMeter。

  • 搭建壓測叢集,平時抓取服務真實流量,節日或大促前進行必要的壓測,以暴露效能瓶頸。

  

日常巡檢/故障演練

用於提前發現問題,如介面掃描、混沌工程就是做這些事情的。

 

大綱就寫到這裡,你還有其他解決方案嗎?歡迎評論區討論。

相關文章