秒殺系統中流量削峯場景應該怎麼解決?

前言

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

可是對秒殺這個場景來講,最終可以搶到商品的人數是固定的,也就是說 100 人和 10000 人發 起請求的結果都是同樣的,併發度越高,無效請求也越多。面試

可是從業務上來講,秒殺活動是但願更多的人來參與的,也就是開始以前但願有更多的人來刷頁 面,可是真正開始下單時,秒殺請求並非越多越好。所以咱們能夠設計一些規則,讓併發的請 求更多地延緩,並且咱們甚至能夠過濾掉一些無效請求。算法

爲何要削峯

爲何要削峯呢?或者說峯值會帶來哪些壞處?數據庫

咱們知道服務器的處理資源是恆定的,你用或者不用它的處理能力都是同樣的,因此出現峯值的 話,很容易致使忙處處理不過來,閒的時候卻又沒有什麼要處理。可是因爲要保證服務質量,我 們的不少處理資源只能按照忙的時候來預估,而這會致使資源的一個浪費。瀏覽器

這就比如由於存在早高峯和晚高峯的問題,因此有了錯峯限行的解決方案。削峯的存在,一是可 以讓服務端處理變得更加平穩,二是能夠節省服務器的資源成本。針對秒殺這一場景,削峯從本 質上來講就是更多地延緩用戶請求的發出,以便減小和過濾掉一些無效請求,它聽從「請求數要 儘可能少」的原則。緩存

今天,我就來介紹一下流量削峯的一些操做思路:排隊、答題、分層過濾。這幾種方式都是無損 (即不會損失用戶的發出請求)的實現方案,固然還有些有損的實現方案,包括咱們後面要介紹 的關於穩定性的一些辦法,好比限流和機器負載保護等一些強制措施也能達到削峯保護的目的, 固然這都是不得已的一些措施,所以就不歸類到這裏了。服務器

排隊

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

用消息隊列來緩衝瞬時流量的方案,以下圖所示:markdown

可是,若是流量峯值持續一段時間達到了消息隊列的處理上限,例如本機的消息積壓達到了存儲 空間的上限,消息隊列一樣也會被壓垮,這樣雖然保護了下游的系統,可是和直接把請求丟棄也 沒多大的區別。就像遇到洪水爆發時,即便是有水庫恐怕也無濟於事。網絡

除了消息隊列,相似的排隊方式還有不少,例如:

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

  2. 先進先出、先進後出等經常使用的內存排隊算法的實現方式;

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

能夠看到,這些方式都有一個共同特徵,就是把「一步的操做」變成「兩步的操做」,其中增長 的一步操做用來起到緩衝的做用。

說到這裏你可能會說,這樣一來增長了訪問請求的路徑啊,並不符合咱們介紹的「4 要 1 不 要」原則。沒錯,的確看起來不太合理,可是若是不增長一個緩衝步驟,那麼在一些場景下系統 極可能會直接崩潰,因此最終仍是須要你作出妥協和平衡。

答題

你是否還記得,最先期的秒殺只是純粹地刷新頁面和點擊購買按鈕,它是後來才增長了答題功能 的。那麼,爲何要增長答題功能呢?

這主要是爲了增長購買的複雜度,從而達到兩個目的。

第一個目的是防止部分買家使用秒殺器在參加秒殺時做弊。2011 年秒殺很是火的時候,秒殺器 也比較猖獗,於是沒有達到全民參與和營銷的目的,因此係統增長了答題來限制秒殺器。增長答 題後,下單的時間基本控制在 2s 後,秒殺器的下單比例也大大降低。答題頁面以下圖所示。

第二個目的其實就是延緩請求,起到對請求流量進行削峯的做用,從而讓系統可以更好地支持瞬 時的流量高峯。這個重要的功能就是把峯值的下單請求拉長,從之前的 1s 以內延長到 2s~10s。 這樣一來,請求峯值基於時間分片了。這個時間的分片對服務端處理併發很是重要,會大大減輕 壓力。並且,因爲請求具備前後順序,靠後的請求到來時天然也就沒有庫存了,所以根本到不了 最後的下單步驟,因此真正的併發寫就很是有限了。這種設計思路目前用得很是廣泛,如當年支 付寶的「咻一咻」、微信的「搖一搖」都是相似的方式。

這裏,我重點說一下秒殺答題的設計思路。

如上圖所示,整個秒殺答題的邏輯主要分爲 3 部分。

  1. 題庫生成模塊,這個部分主要就是生成一個個問題和答案,其實題目和答案自己並不須要很復 雜,重要的是可以防止由機器來算出結果,即防止秒殺器來答題。

  2. 題庫的推送模塊,用於在秒殺答題前,把題目提早推送給詳情繫統和交易系統。題庫的推送主 要是爲了保證每次用戶請求的題目是惟一的,目的也是防止答題做弊。

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

其實真正答題的邏輯比較簡單,很好理解:當用戶提交的答案和題目對應的答案作比較,若是通 過了就繼續進行下一步的下單邏輯,不然就失敗。咱們能夠把問題和答案用下面這樣的 key 來進 行 MD5 加密:

問題 key:userId+itemId+question_Id+time+PK
答案 key:userId+itemId+answer+PK

驗證的邏輯以下圖所示:

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

除了作正確性驗證,咱們還能夠對提交答案的時間作些限制,例如從開始答題到接受答案要超過 1s,由於小於 1s 是人爲操做的可能性很小,這樣也能防止機器答題的狀況。

分層過濾

前面介紹的排隊和答題要麼是少發請求,要麼對發出來的請求進行緩衝,而針對秒殺場景還有一 種方法,就是對請求進行分層過濾,從而過濾掉一些無效的請求。分層過濾其實就是採用「漏 鬥」式設計來處理請求的,以下圖所示。

  1. 假如請求分別通過 CDN、前臺讀系統(如商品詳情繫統)、後臺系統(如交易系統)和數據庫 這幾層,那麼:

  2. 大部分數據和流量在用戶瀏覽器或者 CDN 上獲取,這一層能夠攔截大部分數據的讀取;

  3. 通過第二層(即前臺系統)時數據(包括強一致性的數據)儘可能得走 Cache,過濾一些無效的 請求;

  4. 再到第三層後臺系統,主要作數據的二次檢驗,對系統作好保護和限流,這樣數據量和請求就 進一步減小;

  5. 最後在數據層完成數據的強一致性校驗。

這樣就像漏斗同樣,儘可能把數據量和請求量一層一層地過濾和減小了。

分層過濾的核心思想是:在不一樣的層次儘量地過濾掉無效請求,讓「漏斗」最末端的纔是有效 請求。而要達到這種效果,咱們就必須對數據作分層的校驗。

分層校驗的基本原則是:

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

  2. 對讀數據不作強一致性校驗,減小由於一致性校驗產生瓶頸的問題;

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

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

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

總結

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

  1. 一個是經過隊列來緩衝請求,即控制請求的發出;
  2. 一個是經過答題來延長請求發出的時間, 在請求發出後承接請求時進行控制,最後再對不符合條件的請求進行過濾;
  3. 最後一種是對請求進 行分層過濾。

其中,隊列緩衝方式更加通用,它適用於內部上下游系統之間調用請求不平緩的場景,因爲內部 系統的服務質量要求不能隨意丟棄請求,因此使用消息隊列能起到很好的削峯和緩衝做用。 而答題更適用於秒殺或者營銷活動等應用場景,在請求發起端就控制發起請求的速度,由於越到 後面無效請求也會越多,因此配合後面介紹的分層攔截的方式,能夠更進一步減小無效請求對系 統資源的消耗。

分層過濾很是適合交易性的寫請求,好比減庫存或者拼車這種場景,在讀的時候須要知道還有沒 有庫存或者是否還有剩餘空座位。可是因爲庫存和座位又是不停變化的,因此讀的數據是否必定 要很是準確呢?其實不必定,你能夠放一些請求過去,而後在真正減的時候再作強一致性保證, 這樣既過濾一些請求又解決了強一致性讀的瓶頸。

不過,在削峯的處理方式上除了採用技術手段,其實還能夠採用業務手段來達到必定效果,例如 在零點開啓大促的時候因爲流量太大致使支付系統阻塞,這個時候能夠採用發放優惠券、發起抽 獎活動等方式,將一部分流量分散到其餘地方,這樣也能起到緩衝流量的做用。

最後

你們能夠點贊關注下,之後還會更新更多精選文章分享!

讀者福利

在這給你們推薦一個微信公衆號,那裏天天都會有技術乾貨、技術動向、職業生涯、行業熱點、職場趣事等一切有關於程序員的內容分享。更有海量Java架構、移動互聯網架構相關源碼視頻,面試資料,電子書籍免費發放。我看了以爲資源還不錯,若是大家有須要的話,掃描下方二維碼關注wx公衆號免費獲取↓↓↓


部分資料以下:



相關文章
相關標籤/搜索