流量削峰


如果你看過秒殺系統的流量監控圖的話,你會發現它是一條直線,就在秒殺開始的那一秒是一條很直很直的直線,這時因為秒殺請求在時間上高度集中於某一個特定的時間點。這樣一來,就會導致一個特別高的流量峰值,它對資源的消耗是瞬時的。

對於秒殺這個場景來說,最終能夠搶購到商品的人數是固定的。也就是說,100個人和1000000個人發起請求的結果都是一樣的,最后都只會有100個人買到秒殺的商品。這就意味着,並發量越高,最終的無效請求也就越多。

但是對於業務來說,秒殺活動本身是一種商業推廣行為,肯定是希望有更多的人參與進來的。無效的請求越多,也就表示這商業推廣越成功。只是在秒殺開始的時候,這些大量的秒殺請求就會給承載秒殺的服務器帶來沉重的負擔,因此我們可以設計一些規則,讓並發的請求延緩或過濾掉這些無效請求,以此來削弱流量高峰對服務器的壓力,這就是流量削峰。

為什么要削峰

我們知道,服務器的處理資源是恆定的,你用或者不用,它就在原地,處理能力不會改變。所以出現峰值的話,就很容易會導致忙到處理不過來,雖然閑的時候也會沒什么要處理。但是為了保證服務質量,很多處理資源只能夠按照忙的時候來預估,而這樣就會導致資源的浪費。

流量削峰就好比因為存在早高峰和晚高峰的問題,所以有了錯峰限行的解決方案。

削峰的存在,一是可以讓服務端處理變得平穩,二是可以節省服務器的資源成本。

針對秒殺這一場景,削峰從本質上來說就是更多地去延緩用戶發出的請求,以便減少和過濾掉一些無效的請求,它遵從【請求數要盡量少】的原則。

流量削峰主要有三種操作思路,一是排隊,二是答題,三是分層過濾,這三種方式都是無損(即不會損失用戶的請求發出)的實現方案。當然,還有些有損的實現方案,包括后面要介紹的關於穩定性的一些方法,比如限流和機器負載保護等一些強制措施也能達到削峰保護的目的,只是這些都是一些不得已的措施。

排隊

要對流量進行削峰,最容易想到的解決方案就是用消息隊列來緩沖瞬時流量,把同步的直接調用轉換成異步的間接推送,中間通過一個隊列在一端承接瞬時的流量洪峰,另一端平滑地將消息推送出去。在這里,消息隊列就像一個水庫一樣,攔截上游的洪水,削減進入下游河道的洪峰流量,從而達到減免洪水災害的目的。

使用消息隊列來緩沖瞬時流量的方案,如下圖所示:

但是,如果流量峰值持續一段時間后,達到了消息隊列的處理上限,比如本機的消息積壓達到了存儲空間的上線,消息隊列同樣也會被壓垮。雖然這樣能保護下游的系統,但是和直接把請求丟棄沒有多大區別。就像遇到洪水爆發的時候,即使是有水庫也無濟於事。

而除了消息隊列,類似的排隊方式還有很多,比如

1.利用線程池加鎖等待也是一種常用的排隊方式。

2.先進先出、先進后出等常用的內存排隊算法的實現方式。

3.把請求序列化到文件中,然后再順序地讀文件(例如基於MySQL binlog的同步機制)來恢復請求等方式。

可以看到,這些方式都有一個共同特征,就是把一步的操作變成了兩步的操作,其中增加的一步操作起到緩沖的作用。

但是,因為多加了一層緩沖,就增加了訪問請求的路徑。雖然看起來不太合理,但是如果不增加這個緩沖的步驟,在一些場景下系統就很容易直接奔潰,不符合系統高可用的設計,所以系統設計需要做到妥協和平衡。

答題

最早期的秒殺,只是純粹地刷新頁面和點擊購買按鈕,后來則加上了答題功能。

加上答題功能,是為了增加購買的復雜度,從而達到兩個目的。

第一個目的是防止部分買家在參加秒殺的時候使用秒殺器作弊。在2011年秒殺非常火的時候,秒殺器也比較猖獗,因而並沒有達到全民參與和營銷推廣的目的,所以系統就增加了答題功能來限制秒殺器。在增加了答題功能后,下單的時間基本控制在2s后,秒殺器的下單比例也大大下降。

第二個目的是為了延緩請求,起到對請求流量進行削峰的作用,從而使系統能夠更好地支持瞬時的流量高峰。這個重要的功能就是把峰值的下單請求拉長,從先前的1s之內延長到2s~10s。這樣一來,請求峰值就基於時間分片了。這個時間的分片對服務端處理並發非常重要,會大大減輕壓力。而且由於請求具有先后順序,靠后的請求到來時自然就沒有庫存了,因此根本到不了最后的下單步驟,所以真正的並發寫就非常有限了。這種設計思路目前用得非常普遍,如支付寶的【咻一咻】和微信【搖一搖】,都是類似的方式。

這里要重點說一下秒殺答題的設計思路,看下圖:

整個秒殺答題功能的邏輯,主要可以分為3個部分。

1.題庫生成模塊。這個部分主要就是生成一個個問題和答案,題目和答案本身都不需要太復雜,重要的能防止由機器來算出結果,防止秒殺器答題。

2.題庫推送模塊。這個部分用於在秒殺答題前,把題目提前推送給詳情系統和交易系統。實時推送是為了保證每次用戶請求的題目是唯一的,防止答題作弊。

3.題目的圖片生成模塊,用於把題目生成為圖片格式,並且在圖片里增加一些干擾因素。這也同樣是為了防止機器直接來答題,它要求只有人才能理解題目本身的含義。這里還要注意一點,由於答題時網絡比較擁擠,應該把題目的圖片提前推送到CDN上並且要進行預熱,不然的話當用戶真正請求題目時,圖片可能加載比較慢,從而影響答題的體驗。

事實上,真正答題的邏輯是比較簡單的,比較容易理解。只要將用戶提交的答案與題目對應的答案做比較,比較通過則繼續進行下一步邏輯,否則就失敗。

要注意的是,這里面的驗證邏輯,除了驗證問題的答案之外,還包括用戶本身身份的驗證,例如是否已經登陸、用戶的Cookie是否完整、用戶是否重復頻繁提交等。

更多的,除了做正確性校驗之外,還可以對提交答案的時間做限制。例如從開始答題到接收答案的時間要超過1s,因為1s是認為操作的可能性很小,這樣也能防止機器答題的問題。

分層過濾

前面介紹的排隊和答題,要么是在接收請求時做緩沖,要么是減少請求的同時發送,而針對秒殺場景還有一種方法,就是對請求進行分層過濾,從而過濾掉一些無效的請求。分層過濾其實就是采用【漏斗】式的設計來處理請求。

假如請求分別經過CDN、前台讀系統(如商品詳情系統)、后台系統(如交易系統)和數據庫這層,那么,大部分數據和流量在用戶瀏覽器或CDN上獲取,這一層可以攔截大部分數據的讀取;經過第二層(即前台系統)時,數據(包括強一致性的數據)都盡量走Cache,過濾一些無效的請求;再到第三層的后台系統,主要做數據的二次校驗,對系統做好保護和限流,這樣數據量和請求就進一步減少;最后在數據層完成數據的強一致性校驗就好了。

這樣的話,就像漏斗一樣,盡量把數據量和請求量一層一層地過濾和減少。

分層過濾的核心思想是在不同的層次盡可能地過濾掉無效的請求,讓漏斗最末端的才是有效請求。而要達到這種效果,我們就必須對數據做分層的校驗。

分層校驗的基本原則是:

1.將動態請求的讀數據緩存(Cache)在Web端,過濾掉無效的數據讀取。

2.對讀數據不做強一致性校驗,減少因為一致性校驗產生性能瓶頸的問題。

3.對寫數據進行基於時間的合理分片,過濾掉過期的失效請求。

4.對寫請求做限流保護,將超出系統承載能力的請求過濾掉。

5.對寫數據進行強一致性校驗,只保留最后有效的數據。

分層校驗的目的是在讀系統中盡量減少由於一致性校驗帶來的系統瓶頸,但是盡量將不影響性能的檢查條件提前,比如用戶是否具有秒殺資格、商品狀態是否正常、用戶答題是否正確、秒殺是否已經結束、是否非法請求、營銷等價物是否充足等。而在寫數據系統中,主要對寫的數據(如庫存)做一致性檢查,最后在數據庫層保證數據的最終准確性(如庫存不能為負數)。

總結

上面的內容介紹了如何在網站面臨大流量沖擊時進行請求的削峰,並主要介紹了流量削峰的3種處理方式:

1.通過隊列來緩沖請求,即控制請求的發出。

2.通過答題來延長請求發出的時間,在請求發出后承接請求時進行控制,最后再對不符合條件的請求進行過濾。

3.對請求進行分層過濾,一層一層過濾掉無效請求,使最后到達的有效請求少。

其中,隊列緩沖方式更加通用,它適用於內部上下游系統之間調用請求不平緩的場景,由於內部系統的服務質量要求不能隨意丟棄請求,所以使用消息隊列能起到很好的削峰和緩沖作用。

而答題更適用於秒殺或者營銷活動等應用場景,在請求發起端就控制發起請求的速度,因為越到后面無效請求也會越多,所以配合后面介紹的分層攔截的方式,可以更進一步減少無效請求對系統資源的消耗。

分層過濾非常適合交易性的寫請求,比如減庫存或者拼車這種場景,在讀的時候需要知道還有沒有庫存或者是否還有剩余空座位。但是由於庫存和座位是不停變化的,所以讀的數據是否一定要非常准確嗎?其實不一定,你可以放一些請求過去,然后在真正減的時候再做強一致性保證,這樣既過濾一些請求又解決了強一致性讀的瓶頸。

另外,在削峰的處理方式上除了采用技術手段,其實還可以采用業務手段來達到一定效果,例如在零點開啟大促的時候由於流量太大導致支付系統阻塞,這個時候可以采用發放優惠券、發起抽獎活動等方式,將一部分流量分散到其他地方,這樣也能起到緩沖流量的作用。

 

"有些人離開不用說再見,有些事不開口也明白。"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM