在《微服務系列》中,我們講過很多限流,熔斷相關的知識。
老生長談的一個話題,服務的能力終歸是有限的,無論是記憶體、CPU、執行緒數都是,如果遇到突如其來的峰量請求,我們怎麼友好的使用限流來進行落地,避免整個服務叢集的雪崩。
峰量請求主要有兩種場景:
如果你的服務突然遇到持續性的、高頻率的、不符合預期的突發流量。你需要檢查一下服務是否有被錯誤呼叫、惡意攻擊,或者下游程式邏輯問題。
這種超出預期的呼叫經常會造成你的服務響應延遲,請求堆積,甚至服務雪崩。而雪崩會隨著呼叫鏈向上傳遞,導致整個服務鏈的崩潰,對我們的系統造成很大的隱患。
如果你的商城或者平臺搞活動(類似雙11、618),但你又無法有效評估出這個峰值的具象值和持續時間,那麼你的服務依然有被打垮的風險。
除非你能做到服務叢集彈性伸縮(動態擴縮容)的能力,這個我在後面的雲原生系列中會詳細說。
如上圖,正常數值為1500 QPS,預估模型的量值是2600的QPS,後面發現,來了幾波活動,量值增加到 10000,遠超過我們伺服器的負載。服務一過載,系統就開始出現各種問題,延遲、故障、請求堆積,甚至雪崩。
如果你的服務基架構與足夠強大,你的服務上雲足夠的徹底,那麼彈性伸縮是最好的辦法。類似淘寶、京東、百度APP,都是類似的做法。
kubernetes會根據流量的變化,CPU、記憶體的曲線過程實時計算需要的服務範例數,然後進行動態擴容,低峰期再還回去。這個要求你的服務上雲足夠徹底,並且有足夠的資源來達成資源擴容的目標。
★關於這塊內容筆者後續會有專門的系列來講解,以及如何在大廠實現落地的過程,這邊不展開。
最基本的保障就是能夠在服務做一層保護,避免因為過載而造成服務雪崩,導致整個系統不可用.
對超過預期或者承載能力的流量進行限流是比較好的一種辦法。所以你在雙11、618、搶購、競拍 等的選擇的時候會經常看到這樣的詞彙:
計數演演算法是指再一定的時間間隔裡,記錄請求次數,當時間間隔到期之後,就把計數清零,重新計算。
相對於上一個 計數限流,多了個時間視窗的概念,計數器每過一個時間視窗就重置,重新開始計算。
滑動視窗限流解決固定視窗臨界值的問題,可以保證在任意時間視窗內都不會超過閾值。
類似沙漏思維,大家都用過,沙子是勻速流出得。對於漏桶來說,由於它的出水口的速度是恆定的,也就是消化處理請求的速度是恆定的,所以它可以保證服務以恆定的速率來處理請求,
令牌桶和漏桶的原理類似,只不過漏桶是定速流出,令牌桶是定速流入(即往桶裡塞入令牌),每個請求進來,分配一個令牌,只有拿到了令牌才能進入伺服器處理,拿不到令牌的就被拒絕了。
因為令牌桶的大小也是有限制的,所以一旦令牌桶滿,後續生成的令牌就會被丟棄,拿不到令牌的服務請求就被拒絕了,達到限流的目的。執行原理如下:
fallback:返回固定的物件或者執行固定的方法
// 返回固定的物件
{
"timestamp": 1649756501928,
"status": 429,
"message": "Too Many Requests",
}
// 執行固定的處理方法
function fallBack(Context ctx) {
// Todo 預設的處理邏輯
}
接收到固定的訊息結構或者固定的處理結果之後,以友好的方式提示給使用者。
Redis熱點資料同時並行請求處理,1000W+請求同時投向後端,如果快取未建立,直接投向資料庫,可能會造成擊穿,怎麼破?一般有如下解決方法,優劣各異:
分散式鎖:只允許一個請求執行緒去存取DB,其他請求被阻塞,這樣就避免了很多請求投放到DB上。但是這樣吞吐量降低。
請求按照佇列執行:按照佇列順序執行,避免全部投向資料庫。這樣還是吞吐量降低。
快取預熱:進入資料庫的快取數量會比較少,保證有一部分的資料先做出來。
空/預設 初始值:第一個請求進去的時候,建立空初始值或者預設初始值的快取,並進入資料庫查詢。查詢之後,更新快取,保證後續的拿到正確的值。而在查詢的這個過程中(可能幾ms到幾十ms),拿到的是預設值或空置,前端做一下友好的提示。這是一種降級的策略,保證僅有1個或者前n個進入資料庫。
本地快取:改造web應用服務,在獲取到redis快取後,在web服務本地把熱點的資料進行快取,因為熱點的商品不會很多,所以儲存在本地快取中,是沒有問題的。這樣請求資料時,如果web本地有快取資料,就直接返回了。本地快取和服務程序快取混用的情況。快取更新會增加額外的負擔。
這裡面,空初始值和和分散式鎖的結合使用是目前很多大廠的處理方式,如下:
第一個請求進去的時候,建立空初始值或者預設初始值的快取
後續進來的同類請求會短暫獲取到 預設值的快取資訊
當第一個請求從資料庫查詢到結果,立即更新快取,保證後續的同類請求拿到的是正確的值
而第一個請求呼叫資料庫的過程中(可能幾ms到幾十ms),其他同類請求拿到的是預設值或空值,前端做一下友好的提示
這是一種典型的降級策略,保證僅有1個或者前n個進入資料庫
無論是應用層級還是儲存層級,無非是跟前端約定一個規則,返回預設引數或者預設回到方法,讓前端以使用者友好體驗的方式進行降級,保證伺服器端不至於崩潰。