秒殺系統架構要素

秒殺是電子商務網站常見的一種營銷手段。

原則

不要整個系統宕機。
即使系統故障,也不要將錯誤數據展示出來。
儘量保持公平公正。

實現效果

秒殺開始前,搶購按鈕爲活動未開始。
秒殺開始時,搶購按鈕可以點擊下單。
秒殺結束後,按鈕按鈕變成秒殺已結束。

技術攻關

短時間內的大訪問量對現有網站業務造成的衝擊。
秒殺是一個網站營銷的一個附加活動,時間短,併發量大。
如果和網站原有應用部署在一起,必然會對現有業務造成衝擊,稍有不慎可能導致整個網站癱瘓。

高併發下對服務器數據庫造成的極大負載壓力。
用戶秒殺開始前,通過不斷刷新瀏覽器來保證不會錯過秒殺活動。
頻繁的訪問程序、數據庫會對應用服務器和數據庫服務器造成負載壓力。

網絡帶寬的問題比超過平時好多倍。
如果秒殺頁面的大小爲200K,如果最大併發數爲10000次,那麼需要的網絡和服務器帶寬是2G(200K×10000)。
這些網絡帶寬是因爲秒殺活動新增的,超過網站平時使用的帶寬。

避免直接下單。

秒殺的遊戲規則是到了秒殺才能開始對商品下單購買,在此時間點之前,只能瀏覽信息不可下單。
而下單頁面也是一個普通的URL,如果得到這個URL,不用等到秒殺開始就可以下單了。

應對策略

秒殺系統獨立部署
爲了避免短時間內的大訪問量對現有網站業務造成的衝擊,可以將秒殺系統獨立部署。
如果需要還可以使用獨立域名,使其與網站完全隔離。
即使秒殺系統崩潰了,也不會對網站造成影響。

秒殺商品頁面靜態化

將商品描述、參數、詳情,全部寫到一個靜態頁面,不用進行程序的邏輯處理,不需訪問數據庫。
不用部署動態的服務器和數據庫服務器。

租借秒殺活動的網絡帶寬

因爲秒殺新增的網絡帶寬,必須和運營商重新購買或租借帶寬。
爲了減輕服務器的壓力,需要將秒殺商品頁面緩存在CDN,同樣CDN服務器也需要臨時租借帶寬。

動態生成隨機下單頁面的URL

爲了避免用戶直接訪問下單URL,需要將URL動態化,用隨機數作爲參數,只能秒殺開始的時候才生成。

架構設計

如何控制秒殺商品頁面搶購按鈕的可用/禁用。

購買按鈕只有在秒殺開始的時候才能點亮,在此之前是灰色的,顯示活動未開始。
如果頁面是動態生成的,每次刷新都要請求服務器,那麼勢必造成服務端的負載壓力。
如果頁面是靜態頁面的話,可以將頁面緩存在CDN,反向代理服務器上,甚至用戶瀏覽器上。
但是這樣,秒殺開始時,用戶刷新頁面,根本請求不到應用服務器。

解決方案:

使用JS腳本控制,在頁面中引用一個JS文件(文件極小),但是該文件不要被緩存。
該JS的作用是,包含秒殺開始標誌,修改樣式,生成下單頁面的URL及隨機參數。
該JS文件不被緩存的做法:xxx.js?v=隨機數。
會有一臺服務器進行監控(定時上下架):
當秒殺活動開始時推送該文件。
當秒殺活動結束時推送文件,標示結束標誌,修改樣式。
如下圖。

如何只允許,第一個提交的單進入訂單系統。
由於秒殺到商品的用戶只有一個,因此需要在提交訂單時,進行下單前置檢查。
如果已經有訂單提交成功,表示活動結束,進入秒殺結束頁面。
事實上,訂單數只能有一個,爲了減輕下單頁面服務器的負載壓力,可以控制進入下單頁面的入口。
只有少數用戶能進入下單頁面,其他用戶直接進入秒殺結束頁面。
(前置檢查邏輯)檢查本機已處理的下單請求數目:
如果超過10條,直接返回已結束頁面給用戶。
如果未超過10條,則用戶可進入填寫訂單及確認頁面。
(前置檢查邏輯)檢查全局已提交訂單數目:
已超過秒殺商品總數,返回秒殺結束頁面。
未超過秒殺商品總數,提交到子訂單系統。

減庫存的操作

拍下減庫存(用戶體驗好)

付款減庫存

#

另一種思路也值得學習
《秒殺系統架構優化思路》

上週參加Qcon,有個兄弟分享秒殺系統的優化,其觀點有些贊同,大部分觀點卻並不同意,結合自己的經驗,談談自己的一些看法。

一、爲什麼難
秒殺系統難做的原因:庫存只有一份,所有人會在集中的時間讀和寫這些數據。
例如小米手機每週二的秒殺,可能手機只有1萬部,但瞬時進入的流量可能是幾百幾千萬。
又例如12306搶票,亦與秒殺類似,瞬時流量更甚。

二、常見架構

流量到了億級別,常見站點架構如上:
1)瀏覽器端,最上層,會執行到一些JS代碼
2)站點層,這一層會訪問後端數據,拼html頁面返回給瀏覽器
3)服務層,向上遊屏蔽底層數據細節
4)數據層,最終的庫存是存在這裏的,mysql是一個典型

三、優化方向
1)將請求儘量攔截在系統上游:傳統秒殺系統之所以掛,請求都壓倒了後端數據層,數據讀寫鎖衝突嚴重,併發高響應慢,幾乎所有請求都超時,流量雖大,下單成功的有效流量甚小【一趟火車其實只有2000張票,200w個人來買,基本沒有人能買成功,請求有效率爲0】
2)充分利用緩存:這是一個典型的讀多些少的應用場景【一趟火車其實只有2000張票,200w個人來買,最多2000個人下單成功,其他人都是查詢庫存,寫比例只有0.1%,讀比例佔99.9%】,非常適合使用緩存

四、優化細節
4.1)瀏覽器層請求攔截
點擊了“查詢”按鈕之後,系統那個卡呀,進度條漲的慢呀,作爲用戶,我會不自覺的再去點擊“查詢”,繼續點,繼續點,點點點。。。有用麼?平白無故的增加了系統負載(一個用戶點5次,80%的請求是這麼多出來的),怎麼整?
a)產品層面,用戶點擊“查詢”或者“購票”後,按鈕置灰,禁止用戶重複提交請求
b)JS層面,限制用戶在x秒之內只能提交一次請求
如此限流,80%流量已攔。

4.2)站點層請求攔截與頁面緩存
瀏覽器層的請求攔截,只能攔住小白用戶(不過這是99%的用戶喲),高端的程序員根本不喫這一套,寫個for循環,直接調用你後端的http請求,怎麼整?
a)同一個uid,限制訪問頻度,做頁面緩存,x秒內到達站點層的請求,均返回同一頁面
b)同一個item的查詢,例如手機車次,做頁面緩存,x秒內到達站點層的請求,均返回同一頁面
如此限流,又有99%的流量會被攔截在站點層

4.3)服務層請求攔截與數據緩存
站點層的請求攔截,只能攔住普通程序員,高級黑客,假設他控制了10w臺肉雞(並且假設買票不需要實名認證),這下uid的限制不行了吧?怎麼整?
a)大哥,我是服務層,我清楚的知道小米只有1萬部手機,我清楚的知道一列火車只有2000張車票,我透10w個請求去數據庫有什麼意義呢?對於寫請求,做請求隊列,每次只透有限的寫請求去數據層,如果均成功再放下一批,如果庫存不夠則隊列裏的寫請求全部返回“已售完”
b)對於讀請求,還要我說麼?cache抗,不管是memcached還是redis,單機抗個每秒10w應該都是沒什麼問題的
如此限流,只有非常少的寫請求,和非常少的讀緩存mis的請求會透到數據層去,又有99.9%的請求被攔住了

4.4)數據層閒庭信步
到了數據這一層,幾乎就沒有什麼請求了,單機也能扛得住,還是那句話,庫存是有限的,小米的產能有限,透這麼多請求來數據庫沒有意義。

五、總結
沒什麼總結了,上文應該描述的非常清楚了,對於秒殺系統,再次重複下筆者的兩個架構優化思路:
1)儘量將請求攔截在系統上游
2)讀多寫少的常用多使用緩存

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章