秒杀的实现原理及实现方式
目錄
什么是秒殺
秒殺系統場景特點
秒殺業務分析
秒殺架構設計理念
架構方案
一般秒殺系統架構
?
秒殺技術挑戰
設計思路
前端方案
后端方案
服務端控制器層(網關層)
? ? ? ?服務層
數據庫層
什么是秒殺
?
秒殺場景一般會在電商網站舉行一些活動或者節假日在12306網站上搶票時遇到。對于電商網站中一些稀缺或者特價商品,電商網站一般會在約定時間點對其進行限量銷售,因為這些商品的特殊性,會吸引大量用戶前來搶購,并且會在約定的時間點同時在秒殺頁面進行搶購。
秒殺系統場景特點
- 秒殺時大量用戶會在同一時間同時進行搶購,網站瞬時訪問流量激增。
- 秒殺一般是訪問請求數量遠遠大于庫存數量,只有少部分用戶能夠秒殺成功。
- 秒殺業務流程比較簡單,一般就是下訂單減庫存。
秒殺業務分析
正常電子商務流程
(1)查詢商品;(2)創建訂單;(3)扣減庫存;(4)更新訂單;(5)付款;(6)賣家發貨
秒殺業務的特性
(1)低廉價格;(2)大幅推廣;(3)瞬時售空;(4)一般是定時上架;(5)時間短、瞬時并發量高;
?
秒殺架構設計理念
限流:?鑒于只有少部分用戶能夠秒殺成功,所以要限制大部分流量,只允許少部分流量進入服務后端。
削峰:對于秒殺系統瞬時會有大量用戶涌入,所以在搶購一開始會有很高的瞬間峰值。高峰值流量是壓垮系統很重要的原因,所以如何把瞬間的高流量變成一段時間平穩的流量也是設計秒殺系統很重要的思路。實現削峰的常用的方法有利用緩存和消息中間件等技術。
異步處理:秒殺系統是一個高并發系統,采用異步處理模式可以極大地提高系統并發量,其實異步處理就是削峰的一種實現方式。
內存緩存:秒殺系統最大的瓶頸一般都是數據庫讀寫,由于數據庫讀寫屬于磁盤IO,性能很低,如果能夠把部分數據或業務邏輯轉移到內存緩存,效率會有極大地提升。
可拓展:當然如果我們想支持更多用戶,更大的并發,最好就將系統設計成彈性可拓展的,如果流量來了,拓展機器就好了。像淘寶、京東等雙十一活動時會增加大量機器應對交易高峰。
架構方案
一般秒殺系統架構
?
秒殺技術挑戰
假設某網站秒殺活動只推出一件商品,預計會吸引1萬人參加活動,也就說最大并發請求數是10000,秒殺系統需要面對的技術挑戰有
對現有網站業務造成沖擊
秒殺活動只是網站營銷的一個附加活動,這個活動具有時間短,并發訪問量大的特點,如果和網站原有應用部署在一起,必然會對現有業務造成沖擊,稍有不慎可能導致整個網站癱瘓。
解決方案:將秒殺系統獨立部署,甚至使用獨立域名,使其與網站完全隔離。?
高并發下的應用、數據庫負載
用戶在秒殺開始前,通過不停刷新瀏覽器頁面以保證不會錯過秒殺,這些請求如果按照一般的網站應用架構,訪問應用服務器、連接數據庫,會對應用服務器和數據庫服務器造成負載壓力。
解決方案:重新設計秒殺商品頁面,不使用網站原來的商品詳細頁面,頁面內容靜態化,用戶請求不需要經過應用服務。?
突然增加的網絡及服務器帶寬
假設商品頁面大小200K(主要是商品圖片大小),那么需要的網絡和服務器帶寬是2G(200K×10000),這些網絡帶寬是因為秒殺活動新增的,超過網站平時使用的帶寬。
解決方案:因為秒殺新增的網絡帶寬,必須和運營商重新購買或者租借。為了減輕網站服務器的壓力,需要將秒殺商品頁面緩存在CDN,同樣需要和CDN服務商臨時租借新增的出口帶寬。?
直接下單
秒殺的游戲規則是到了秒殺才能開始對商品下單購買,在此時間點之前,只能瀏覽商品信息,不能下單。而下單頁面也是一個普通的URL,如果得到這個URL,不用等到秒殺開始就可以下單了。
解決方案:為了避免用戶直接訪問下單頁面URL,需要將改URL動態化,即使秒殺系統的開發者也無法在秒殺開始前訪問下單頁面的URL。辦法是在下單頁面URL加入由服務器端生成的隨機數作為參數,在秒殺開始的時候才能得到。?
如何控制秒殺商品頁面購買按鈕的點亮
購買按鈕只有在秒殺開始的時候才能點亮,在此之前是灰色的。如果該頁面是動態生成的,當然可以在服務器端構造響應頁面輸出,控制該按鈕是灰色還 是點亮,但是為了減輕服務器端負載壓力,更好地利用CDN、反向代理等性能優化手段,該頁面被設計為靜態頁面,緩存在CDN、反向代理服務器上,甚至用戶瀏覽器上。秒殺開始時,用戶刷新頁面,請求根本不會到達應用服務器。
解決方案:使用JavaScript腳本控制,在秒殺商品靜態頁面中加入一個JavaScript文件引用,該JavaScript文件中包含 秒殺開始標志為否;當秒殺開始的時候生成一個新的JavaScript文件(文件名保持不變,只是內容不一樣),更新秒殺開始標志為是,加入下單頁面的URL及隨機數參數(這個隨機數只會產生一個,即所有人看到的URL都是同一個,服務器端可以用redis這種分布式緩存服務器來保存隨機數),并被用戶瀏覽器加載,控制秒殺商品頁面的展示。這個JavaScript文件的加載可以加上隨機版本號(例如xx.js?v=32353823),這樣就不會被瀏覽器、CDN和反向代理服務器緩存。這個JavaScript文件非常小,即使每次瀏覽器刷新都訪問JavaScript文件服務器也不會對服務器集群和網絡帶寬造成太大壓力。?
如何只允許第一個提交的訂單被發送到訂單子系統
由于最終能夠成功秒殺到商品的用戶只有一個,因此需要在用戶提交訂單時,檢查是否已經有訂單提交。如果已經有訂單提交成功,則需要更新 JavaScript文件,更新秒殺開始標志為否,購買按鈕變灰。事實上,由于最終能夠成功提交訂單的用戶只有一個,為了減輕下單頁面服務器的負載壓力, 可以控制進入下單頁面的入口,只有少數用戶能進入下單頁面,其他用戶直接進入秒殺結束頁面。
解決方案:假設下單服務器集群有10臺服務器,每臺服務器只接受最多10個下單請求。在還沒有人提交訂單成功之前,如果一臺服務器已經有十單了,而有的一單都沒處理,可能出現的用戶體驗不佳的場景是用戶第一次點擊購買按鈕進入已結束頁面,再刷新一下頁面,有可能被一單都沒有處理的服務器處理,進入了填寫訂單的頁面,可以考慮通過cookie的方式來應對,符合一致性原則。當然可以采用最少連接的負載均衡算法,出現上述情況的概率大大降低。?
如何進行下單前置檢查
下單服務器檢查本機已處理的下單請求數目:
? ? ? ? ? ? 1) 如果超過10條,直接返回已結束頁面給用戶;
? ? ? ? ? ? 2) 如果未超過10條,則用戶可進入填寫訂單及確認頁面;
?
? ? ? ? ?檢查全局已提交訂單數目:
? ? ? ? ? ?1) 已超過秒殺商品總數,返回已結束頁面給用戶;
? ? ? ? ? ?2) 未超過秒殺商品總數,提交到子訂單系統;
? ? 8. 秒殺一般是定時上架
? ? ? ?該功能實現方式很多。不過目前比較好的方式是:提前設定好商品的上架時間,用戶可以在前臺看到該商品,但是無法點擊“立即購買”的按鈕。但是需要考慮的是,有人可以繞過前端的限制,直接通過URL的方式發起購買,這就需要在前臺商品頁面,以及bug頁面到后端的數據庫,都要進行時鐘同步。越在后端控制,安全性越高。
定時秒殺的話,就要避免賣家在秒殺前對商品做編輯帶來的不可預期的影響。這種特殊的變更需要多方面評估。一般禁止編輯,如需變更,可以走數據訂正多的流程。
減庫存的操作
有兩種選擇,一種是拍下減庫存?另外一種是付款減庫存;目前采用的“拍下減庫存”的方式,拍下就是一瞬間的事,對用戶體驗會好些。
庫存會帶來“超賣”的問題:售出數量多于庫存數量
由于庫存并發更新的問題,導致在實際庫存已經不足的情況下,庫存依然在減,導致賣家的商品賣得件數超過秒殺的預期。方案:采用樂觀鎖
update auction_auctions setquantity = #inQuantity#where auction_id = #itemId# and quantity = #dbQuantity#還有一種方式,會更好些,叫做嘗試扣減庫存,扣減庫存成功才會進行下單邏輯
update auction_auctions setquantity = quantity-#count#where auction_id = #itemId# and quantity >= #count#秒殺器的應對
秒殺器一般下單個購買及其迅速,根據購買記錄可以甄別出一部分。可以通過校驗碼達到一定的方法,這就要求校驗碼足夠安全,不被破解,采用的方式有:秒殺專用驗證碼,電視公布驗證碼,秒殺答題。
設計思路
將請求攔截在系統上游,降低下游壓力:秒殺系統特點是并發量極大,但實際秒殺成功的請求數量卻很少,所以如果不在前端攔截很可能造成數據庫讀寫鎖沖突,甚至導致死鎖,最終請求超時。?
充分利用緩存:利用緩存可極大提高系統讀寫速度。?
消息隊列:消息隊列可以削峰,將攔截大量并發請求,這也是一個異步處理過程,后臺業務根據自己的處理能力,從消息隊列中主動的拉取請求消息進行業務處理。
前端方案
瀏覽器端(js):
頁面靜態化:將活動頁面上的所有可以靜態的元素全部靜態化,并盡量減少動態元素。通過CDN來抗峰值。?
禁止重復提交:用戶提交之后按鈕置灰,禁止重復提交?
用戶限流:在某一時間段內只允許用戶提交一次請求,比如可以采取IP限流
后端方案
服務端控制器層(網關層)
限制uid(UserID)訪問頻率:我們上面攔截了瀏覽器訪問的請求,但針對某些惡意攻擊或其它插件,在服務端控制層需要針對同一個訪問uid,限制訪問頻率。
? ? ? nginx請求限制模塊
? ? ? ngx_http_limit_conn_module?
限制連接數模塊通常用來限制同一IP地址的可并發連接數指令說明:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html需要注意的是$binary_remote_addr而不是$remote_addr,$remote_addr的長度為7到15個字節,它的會話信息的長度為32或64 bytes,$binary_remote_addr的長度為4字節,會話信息的長度為32字節,這樣設置1M的一個zone時,用$binary_remote_addr方式,該zone將會存放32000個會話。? ? ?ngx_http_limit_req_module
限制請求數模塊通常用來限制同一IP地址單位時間可完成的請求數,限制的方法是采用漏桶算法(Leaky Bucket),每秒處理固定請求數量,推遲過多請求,超過桶的閥值,請求直接終止返回503。指令說明:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html? ?基于nginx的Tengine分支ngx_http_limit_req_module
nginx類似,不過支持多個變量,并且支持多個limit_req_zone及forbid_action的設置。指令說明:http://tengine.taobao.org/document_cn/http_limit_req_cn.html? ?基于nginx的Senginx分支的ngx_http_limit_req_module
指令說明:http://www.senginx.org/cn/index.php/%E5%9F%BA%E4%BA%8E%E6%9D%A1%E4%BB%B6%E7%9A%84%E9%99%90%E9%80%9F%E5%8A%9F%E8%83%BD稱之為基于條件的限速功能,在Tenginer的limit_req模塊基礎上,增加condition參數,在條件為真時執行限制動作。? ?基于nginx的Senginx分支的ngx_http_ip_behavior
指令說明:http://www.senginx.org/cn/index.php/%E8%AE%BF%E9%97%AE%E8%A1%8C%E4%B8%BA%E8%AF%86%E5%88%AB%E6%A8%A1%E5%9D%97稱之為行為識別模塊,訪問行為識別模塊的作用是對用戶訪問網站的行為進行監控? ?基于nginx的Senginx分支的ngx_http_robot_mitigation
指令說明:http://www.senginx.org/cn/index.php/Robot_Mitigation%E6%A8%A1%E5%9D%97稱之為HTTP機器人緩解,Robot Mitigation模塊采用了一種基于“挑戰”的驗證方法,即向客戶端發送特定的、瀏覽器能解析的應答,如果客戶端是真實的瀏覽器,則會重新觸發請求, 并帶有一個特定的Cookie值,Robot Mitigation模塊會依據此Cookie的信息來決定是否放行此請求。? ? ? ?服務層
上面只攔截了一部分訪問請求,當秒殺的用戶量很大時,即使每個用戶只有一個請求,到服務層的請求數量還是很大。比如我們有100W用戶同時搶100臺手機,服務層并發請求壓力至少為100W。
??????1、把需要秒殺的商品的主要信息以及庫存初始化到redis緩存中
??????2、做請求合法性的校驗(比如是否登錄),如果請求非法,直接給前端返回錯誤碼,進行相應的提示
??????3、進行內存標識的判斷(true 已經秒殺結束,false 未秒殺結束),如果內存標識為true,直接返回秒殺結束
??????4、decr 進行預減庫存操作,判斷,如果decr后庫存量小于0,則把內存標記置為true(已經秒殺結束),且返回秒殺結束
??????5、判斷是否已經秒殺到了,防止重復秒殺,如果重復秒殺,直接返回重復秒殺的錯誤碼
??????6、發送秒殺到的MQ消息給相應的業務端進行處理,并給用戶端返回排隊中,如果客戶端收到排隊中的消息,則自動進行輪詢查詢,直到返回秒殺成功或者秒殺失敗為止
??????7、相應的業務端進行處理:真正處理秒殺的業務端,再次進行校驗(比如秒殺是否結束,庫存是否充足等)、將用戶和商品id作為key存入redis來標識該用戶秒殺該商品成功(上述的第5步會用到)、減庫存、生成秒殺訂單、返回秒殺成功
???????????注意:就算請求走到了真正處理業務的這一端,也有可能秒殺失敗,比如秒殺結束,庫存不足,真正減庫存失敗,秒殺單生成失敗等等,一旦失敗,則返回秒殺結束
優化:將秒殺接口隱藏:用戶點擊秒殺按鈕的時候,根據用戶id生成唯一的加密串存入緩存并返回給客戶端,然后客戶端再次請求的時候帶著加密串過來,后端進行校驗是否合法,若不合法,直接返回請求非法;
???????????限制某個接口的訪問頻率:可以用攔截器配合自定義注解來實現,這么做可以和具體的業務分離減少入侵,使用起來也非常方便
?
數據庫層
數據庫層是最脆弱的一層,一般在應用設計時在上游就需要把請求攔截掉,數據庫層只承擔“能力范圍內”的訪問請求。所以,上面通過在服務層引入隊列和緩存,讓最底層的數據庫高枕無憂
為防止秒殺出現負數訂單數大于真正的庫存數,所以在真正減庫存,update庫存的時候應該加上where 庫存>0,而且需要給秒殺訂單表加上用戶id和商品id聯合的唯一索引
?
總結
以上是生活随笔為你收集整理的秒杀的实现原理及实现方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【信息系统项目管理师 - 备考宝典 -
- 下一篇: 找钱问题