前面已經寫了很多億級流量的文章, 中間講了各種處理思路, 這兒將這些思路與業務綜合起來, 情形一就是秒殺, 提到秒殺, 很多人都會覺得這是一件技術要求很高的事情, 因為這涉及到超大訪問量(可能瞬間千萬倍的使用者訪問商品)、維護資料一致性(不能超賣), 前者對效能有極高的要求, 而後者又正好拉低了效能,本文談談秒殺的設計思路, 並在最後給出秒殺設計的簡單模型圖。
秒殺的情景
生活中有很多秒殺的情景, 例如商家促銷, 像一元搶茅臺, 五毛錢搶寶馬(這兒只是一個例子), 假如一百萬人來搶十瓶茅臺, 這時候肯定不能多賣出, 也就是不能被大於10的人數搶到, 通常最後時間會有一個倒數計時按鈕, 30...29...28......3...2...1 GO! 之後躍躍欲試的人們開始搶。
這時候有以下問題需要被考慮 :
第一是多個客戶端的時間如何保持同步, 也就是讓大家看到時間是一致的, 不能你顯示3, 而我這還顯示 30 。
第二是如何保證有沒有黃牛用機器人搶 。
第三是如何確保後端伺服器可以支撐住這巨大的流量。
......
秒殺解決思路
有了上面的情景以及引出來的問題, 來看看秒殺方案的設計思路, 我們伺服器如何應對這一百萬的TPS呢?
首先想到的是擴容, 詳情可以參考伺服器擴容思路及問題分析 , 但這是不現實的, 因為擴容需要很多很多機器, TPS增加一萬倍對物理伺服器的效能要求遠遠不止一萬倍, 另外對於一個商家來說, 為了這一次促銷活動購置伺服器是不划算的, 平時勢必有眾多的機器處於閒置狀態。
沒法擴容, 那麼也就意味著要使用其他方法, 如果所有請求訪問一臺物理機器肯定不行, 一百萬的資料訪問無論如何分庫分表都無濟於事, 因為面對的每一條都是熱點資料, 所以要用到分散式架構的思路。
秒殺的技術方案
分散式, 可以降低伺服器的壓力, 下面開始講述秒殺的設計思路。
方案一
很明顯, 要讓一百萬使用者能夠同時開啟搶貨的網頁, 勢必要用要到CDN(內容分發網路, 對這個概念不清楚的話可以參考:全域性負載均衡與CDN內容分發), CDN主要作用有兩個, 一方面是將一些不會改變的靜態資源放到離客戶端較近的邊緣伺服器上, 這樣客戶端請求資料的時候可以直接從邊緣伺服器獲取, 降低中心伺服器的壓力, 另外一方面可以把小服務部署到 CDN 結點上去,這樣,當前端頁面來問開沒開始時,這個小服務除了告訴前端開沒開始外,它還可以統計下有多少人線上。每個小服務會把當前線上等待秒殺的人數每隔一段時間就回傳給我們的資料中心,於是我們就知道全網總共線上的人數有多少。
假設,我們知道有大約 100 萬的人線上等著搶,那麼,在我們快要開始的時候,由資料中心向各個部署在 CDN 結點上的小服務上傳遞一個概率值,這個概率值為CDN節點人數權重乘以獲獎概率, 比如說是\(e\)。於是,當秒殺開始的時候,這 100 萬使用者都在點下單按鈕,首先他們請求到的是 CDN 上的這些服務,這些小服務按照 \(e\) 的量把使用者放到後面的資料中心,也就是放過去 \(人數*e\),剩下的都直接返回秒殺已結束。
方案二
利用我們分散式中限流、閘道器等知識, 將請求層層篩選, 降低最後連線到資料庫的請求。
首先也是利用CDN將靜態資源分發在邊緣伺服器上, 當進行服務請求時, 先進行鑑權, 鑑權主要是篩選機器人等非人工搶購, 根據實際經驗, 鑑權可以篩選很大一部分使用者, 例如是否登入。
當鑑權確定是真實有效的使用者之後, 通過負載均衡(詳情可以參考LVS負載均衡理論以及演算法概要)也就是LVS+Keepalived 將請求分配到不同的Nginx上,一般會建立Nginx叢集, 然後再通過閘道器叢集, 即使這樣還是要增加一些限流措施, 如果到這一步還是有很多請求壓到資料庫勢必撐不住, 那麼可以採取服務限流、服務降級等措施,進行削峰處理。到這兒理論上流量就不高了, 如果還是很高, 後面就將熱點資料放進快取叢集中進行預熱, 同時設定定時任務,一方面關注資料庫與快取的一致性, 另一方面關閉超時未支付的訂單, 當訂單提交之後 交給任務佇列, 生成訂單、修改資料庫、做好持久化工作。
架構圖:
這就是整個“秒殺”的技術細節,是不是有點不敢相信?
與這種秒殺業務類似的還有12306搶票, 這個也是瞬間高流量, 但是上面提到的架構就不適合了,因為12306完全不知道使用者來是要買哪張火車票的。不知道這個資訊,很不好過濾使用者,而且使用者在買票前需要有很多查詢操作,然後在查詢中選擇自己的車票。也就意味著沒法在開始就過濾使用者。
12306 最好的應對方式,除了不要一次把所有的票放出來,而是分批在不同的時間段把票放出來,這樣可以讓人們不要集中在一個時間點來搶票,做到人肉分流,可以降低一些併發度。
另外,12306 最好是用預售的方式,讓大家把自己的購票先輸入到系統中。系統並不真正放票,而是把大家的需求都收集好,然後做整體統籌安排,該增加車次的增加車次,該加車廂的加車廂,這樣可以確保大家都能走。實在不行,就抽籤了。
總結
我們可以看到,解決秒殺這種特定業務場景,可以使用 CDN 的邊緣結點來扛流量,然後過濾使用者請求(限流使用者請求),來保護資料中心的系統,這樣才讓整個秒殺得以順利進行。也可以像方案二那樣逐層過濾請求, 這種業務場景和雙十一相同嗎? 如果像雙 11 那樣,想盡可能多地賣出商品,
那麼就不像秒殺了。這是要儘可能多地收訂單,但又不能超過庫存,其中還有大量的銀行支付,各大倉庫的庫存查詢和分配,這些都是非常慢的操作。為了保證一致性,還要能夠扛得住像雙 11 這樣的大規模併發訪問,那麼,應該怎麼做呢?
使用秒殺這樣的解決方案基本上不太科學了。這個時候就需要認認真真地做高併發的架構和測試了,需要各個系統把自己的效能調整上去,還要小心地做效能規劃,更要把分散式的彈力設計做好,最後是要不停地做效能測試,找到整個架構的系統瓶頸,然後不斷地做水平擴充套件,以解決大規模的併發。
有些時候,我們總是在想資料中心的解決方案。其實,我們有時候也需要換一換思路,也許,在資料中心解決並不一定是最好的方式,放在邊緣來解決可能會更好一些。尤其是針對一些有地域特徵的業務,比如像外賣、共享單車、叫車這樣的業務。其實,把一些簡單的業務邏輯放在邊緣,比放在資料中心不但能夠有更好的效能,還有更便宜的成本。我覺得,隨著請求量越來越大,資料也越來越多,資料中心是有點到瓶頸了,而需要邊緣結
點來幫忙了。而且,這個邊緣化解決方案的趨勢也會越來越有優勢。