面對突發流量,保證服務可用的4個手段

2023.02.27

面對突發流量,保證服務可用的4個手段


面對這種突發流量的場景,核心思路就是要優先保證優核心業務和優先保證絕大部分用戶。常見的應對手段有四種,降級、熔斷、限流和排隊,下面我會一一講解。

前言

不知道你有沒有這樣的經歷,線上的系統突然來了很大的流量,有可能是黑客的攻擊,也有可能是業務量遠遠大於你的預估,如果你的系統沒有做任何的防護措施,這時候系統負載過高,系統資源慢慢耗盡,接口響應越來越慢,直至不可用,這又導致了調用你接口的上游系統發生資源耗盡的情況,最終導致系統雪崩。想想就知道,這是一個災難性的後果,那麼有什麼方法呢?

面對這種突發流量的場景,核心思路就是要優先保證優核心業務和優先保證絕大部分用戶。常見的應對手段有四種,降級、熔斷、限流和排隊,下面我會一一講解。

1. 降級

降級指系統將某些業務或者接口的功能降低,可以是只提供部分功能,也可以是完全停掉所有功能,優先保證核心功能。

比如淘寶雙11零點搶購的時候你會發現商品的退貨功能不可以使用了。又比如論壇可以降級為只能看帖子,不能發帖子;也可以降級為只能看帖子和評論,不能發評論;

常見的實現降級的方式有兩種:

  • 系統後門降級

簡單來說,就是系統預留了後門用於降級操作。例如,系統提供一個降級URL,當訪問這個URL時,就相當於執行降級指令,具體的降級指令通過URL的參數傳入即可。這種方案有一定的安全隱患,所以也會在URL中加入密碼這類安全措施。

系統後門降級的方式實現成本低,但主要缺點是如果服務器數量多,需要一台一台去操作,效率比較低,這在故障處理爭分奪秒的場景下是比較浪費時間的。

  • 獨立降級系統

為了解決系統後門降級方式的缺點,我們可以將降級操作獨立到一個單獨的系統中,實現複雜的權限管理、批量操作等功能。

基本架構如下:

圖片

2. 熔斷

熔斷是指按照一定的規則,比如1分鐘內60%的請求響應錯誤就停掉對外部接口的訪問,防止某些外部接口故障導致自己的系統處理能力急劇下降或者出故障。

圖片

熔斷和降級是兩個比較容易混淆的概念,因為單純從名字上看,好像都有禁止某個功能的意思。但它們的內涵是不同的,因為降級的目的是應對系統自身的故障,而熔斷的目的是應對依賴的外部系統故障的情況。

關於服務熔斷的實現,比較主流的有兩種方案,​​Spring Cloud Netflix Hystrix​​和阿里的​​Sentinel​​,我們公司的項目用的是​​Sentinel​​。

  • Hystrix是一個用於處理分佈式系統的延遲和容錯的一個開源庫,在分佈式系統裡,許多依賴不可避免的會調用失敗,比如超時、異常等,Hystrix能保證在一個依賴出現問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分佈式系統的穩定性。
  • Sentinel 是阿里中間件團隊開源的,面向分佈式服務架構的輕量級高可用流量控制組件,主要以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度來幫助用戶保護服務的穩定性。

3. 限流

每個系統都有服務的上線,所以當流量超過服務極限能力時,系統可能會出現卡死、崩潰的情況,所以就有了降級和限流。限流其實就是:當高並發或者瞬時高並發時,為了保證系統的穩定性、可用性,系統以犧牲部分請求為代價或者延遲處理請求為代價,保證系統整體服務可用。

限流一般都是系統內實現的,常見的限流方式可以分為兩類:基於請求限流和基於資源限流。

  • 基於請求限流

基於請求限流指從外部訪問的請求角度考慮限流,常見的方式有兩種。

第一種是限制總量,也就是限制某個指標的累積上限,常見的是限制當前系統服務的用戶總量,例如:某個直播間限制總用戶數上限為100萬,超過100萬後新的用戶無法進入;某個搶購活動商品數量只有100個,限制參與搶購的用戶上限為1萬個,1萬以後的用戶直接拒絕。

第二種是限制時間量,也就是限制一段時間內某個指標的上限,例如1分鐘內只允許10000個用戶訪問;每秒請求峰值最高為10萬。

無論是限制總量還是限制時間量,共同的特點都是實現簡單,但在實踐中面臨的主要問題是比較難以找到合適的閾值。例如係統設定了1分鐘10000個用戶,但實際上6000個用戶的時候系統就扛不住了;或者達到1分鐘10000用戶後,其實係統壓力還不大,但此時已經開始丟棄用戶訪問了。

即使找到了合適的閾值,基於請求限流還面臨硬件相關的問題。例如一台32核的機器和64核的機器處理能力差別很大,閾值是不同的,可能有的技術人員以為簡單根據硬件指標進行數學運算就可以得出來,實際上這樣是不可行的,64核的機器比32核的機器,業務處理性能並不是2倍的關係,可能是1.5倍,甚至可能是1.1倍。

為了找到合理的閾值,通常情況下可以採用性能壓測來確定閾值,但性能壓測也存在覆蓋場景有限的問題,可能出現某個性能壓測沒有覆蓋的功能導致系統壓力很大;另外一種方式是逐步優化:先設定一個閾值然後上線觀察運行情況,發現不合理就調整閾值。

基於上述的分析,根據閾值來限制訪問量的方式更多的適應於業務功能比較簡單的系統,例如負載均衡系統、網關係統、搶購系統等。

  • 基於資源限流

基於請求限流是從系統外部考慮的,而基於資源限流是從系統內部考慮的,也就是找到系統內部影響性能的關鍵資源,對其使用上限進行限制。常見的內部資源包括連接數、文件句柄、線程數和請求隊列等。

例如,採用Netty來實現服務器,每個進來的請求都先放入一個隊列,業務線程再從隊列讀取請求進行處理,隊列長度最大值為10000,隊列滿了就拒絕後面的請求;也可以根據CPU的負載或者佔用率進行限流,當CPU的佔用率超過80%的時候就開始拒絕新的請求。

基於資源限流相比基於請求限流能夠更加有效地反映當前系統的壓力,但實際設計時也面臨兩個主要的難點:如何確定關鍵資源,以及如何確定關鍵資源的閾值。

通常情況下,這也是一個逐步調優的過程:設計的時候先根據推斷選擇某個關鍵資源和閾值,然後測試驗證,再上線觀察,如果發現不合理,再進行優化。

4. 排隊

排隊這種方式,想必大家在熟悉不過了。大家在12306買火車票的時候,是不是會告訴你在排隊中,等待一段時間後才會鎖定車票,付款。年底時,全中國那麼多人買票,12306就是通過排隊機制來搞定的。但是也有缺點,那就是用戶體驗沒那麼好。

由於排隊需要臨時緩存大量的業務請求,單個系統內部無法緩存這麼多數據,一般情況下,排隊需要用獨立的系統去實現,例如使用Kafka這類消息隊列來緩存用戶請求。

圖片

  • 排隊模塊

負責接收用戶的搶購請求,將請求以先入先出的方式保存下來。每一個參加秒殺活動的商品保存一個隊列,隊列的大小可以根據參與秒殺的商品數量(或加點餘量)自行定義。

  • 調度模塊

負責排隊模塊到服務模塊的動態調度,不斷檢查服務模塊,一旦處理能力有空閒,就從排隊隊列頭上把用戶訪問請求調入服務模塊,並負責向服務模塊分發請求。這裡調度模塊扮演一個中介的角色,但不只是傳遞請求而已,它還擔負著調節系統處理能力的重任。我們可以根據服務模塊的實際處理能力,動態調節向排隊系統拉取請求的速度。

  • 服務模塊

負責調用真正業務來處理服務,並返回處理結果,調用排隊模塊的接口回寫業務處理結果。

總結

最後我們通過一個表格在總結以下上面4種保證服務高可用的手段。

圖片

圖片