分享内容搶先看
本次分享介紹了A對搶購業務的設計實踐。主要包括例如以下主題:
1. 搶購業務介紹
2. 詳細搶購項目中的設計
2.1. 怎樣解耦前後端壓力
2.2. 怎樣保證商品庫存可靠
2.3. 怎樣在業務中多放對賬
3. 項目總結
4. Q & A
搶購、閃購,從國外風靡後,國内各類站點都開始做相似的業務,我們耳熟能詳的唯品會、淘寶、京東都有這類業務。搶購,很多其它出如今電商站點。那麼。今天和大家一起學習下搶購業務形态的業務架構設計。
1.搶購業務介紹
我們常見的搶購業務分兩種: 限時搶購、限量搶購,我簡單分了下這些case,例如以下圖:

想必小米的搶購營運的最火爆了。每發一款新品,都限量發售,每次搞的大家心裡癢癢的。記得之前還由于搶購太火爆。網站打不開,崩潰了。那麼問題來了:為什麼搶購總是引發RD、OP恐慌?我了解是。爆品太火爆。瞬時請求太大。導緻業務機器、存儲機器都在搶購高峰時扛了太多壓力。
那麼,我們今天以一個搶購業務場景為例。看看怎樣扛住壓力。做好搶購業務!如果。這時候我們接到了産品層面的需求,例如以下圖:
PM也挺呵呵的。又要有時段的要求、又要有限量的要求。大而全呐。
隻是。對于咱們RD同學,也不是問題。我們一起來看看,怎樣設計業務架構。把需求滿足的棒棒哒。
首先,我們冷靜的看看需求。
需求說:商品資料來自資源方。(哦,我們沒有商品資料。)
需求說:每天要有好幾場搶購,每場搶購都有商品限制(哦,有點商場促銷還限量甩的feel)
需求說:商品要基于使用者位置排序(哦,移動端業務嘛。這樣的需求總是有的)
需求說:balabala……
2.詳細搶購項目中的設計
通過我們先行的搶購需求分析,我們畫一個粗略的流程圖,例如以下:
我們将自身簡單劃為兩部分:業務層、資料層,而且旁路設計一個“營運控制”環節。
當然,資料源自第三方嘛。我們的資料層基于第三方資源資料建構。
這時,我們來看看這個草圖裡幾個庫和幾個資料流,是如何的。
首先,看看庫。
資料層的“商品庫”,顯而易見。用于存儲第三方商品資料。通過第三方推、我們拉的方式來建構這個資料庫資訊。資料庫層的“搶購計劃”庫,主要由旁路的“營運控制”環節産生的資料,由營運同學來維護搶購場次、商品數量。
業務層的“搶購庫”,事實上是商品庫的子集。由營運同學勾選商品并配好該商品放出多少用于搶購,公布到業務層面的搶購庫中。
業務層的Transaction Data,一會我們講到與第三方對賬時候,我們再說它。
2.1 怎樣解耦前後端壓力
我們此時回想下檔案夾。檔案夾中我們講。怎樣隔離前後端壓力呢?做法是:
1. 讓我們業務的壓力,不會傳遞到資源方,避免造成資源方接口壓力同比增長。
是以,我們自己建了商品庫,此時。第三方笑了。
2. 業務層與資料層解耦,我們讓搶購庫位于業務層。讓商品庫位于資料層。由于我們能夠想象到,搶購高峰來暫時,查詢“商品還有沒有?”的請求是最多的。若“有沒有”這樣的高頻請求每次都去資料層,那我們事實上就将業務、資料耦合在一起了,那麼,就有了搶購庫這個子庫,在業務層抗壓力。(這裡能夠明白的是。資料層的商品庫為關系型存儲,業務層的搶購庫為nosql的)
有了業務層的nosql(我們就用redis吧)抗高頻壓力。資料層的商品庫笑了。
這裡就能夠抛一個思想了:我們的架構設計中,須要分解壓力。在網際網路項目中,來自于使用者的大流量不少見,這些流量終于都會落到一個地方。就看我們的設計怎樣分解這個壓力了,怎樣避免它層層傳遞。抛個case,我們的水準分布業務機器,也是考慮通過水準擴充執行個體的方式,來分解大流量壓力。
不扯概念的東西了,我們回歸我們的搶購業務。
有了簡單的分層設計。解決的大家都操心的壓力問題,我們就看看搶購業務的時序是如何的。
我們的時序圖分兩個視角來說明:
1. 商品的角度。
2. 使用者的角度;
商品角度的時序圖。從左到右:資源方、資料層、旁路-營運控制層、業務層。
例如以下圖:
錄入商品 即商品從資源方公布到我們的資料層,形式能夠是通過API、能夠是通過檔案傳輸、能夠是我們去拉去。
通過我們的代碼邏輯,記錄到我們資料層的“商品庫DB”。
有了自建的商品庫的資料,我們的營運同學就能夠基于商品庫設計每天的搶購場次(此事就有Web界面的事情,這裡我們就不展開這塊了),營運同學建立好一批搶購場次,記錄在資料層的“搶購計劃”這一關系型資料庫中。
營運同學建立完搶購場次後。沒完事。還得應産品需求。基于商品庫,配置每場搶購場次中覆寫的商品。及商品的數量。這些搶購場次内的商品配置,會簡單的記錄在業務層的“搶購庫”中。(搶購庫記錄的資訊較為簡單,比如商品庫中ID為123123的商品有100件,業務層的搶購庫中僅僅存ID 123123商品營運配了在第X場搶購中有5件)
此時,資料層的 商品庫有了資源方資料、資料層的 搶購計劃庫中有營運配的搶購計劃。業務層的搶購庫中每場搶購活動中商品的情況。
那麼,業務層此時就能夠基于時間。來展示營運配的搶購場次了。
業務層,怎樣展示,這塊就是拼裝資料、前端效果了,這裡也不展開了。
如果此事某場場次的搶購活動已經開始,我們再看看使用者角度的時序圖:
使用者點選某個商品的搶購button。業務層代碼首先去看看搶購計劃庫此時是否開始(此步可緩存、也可cache在前端頁面或Client。若有cache的話,此步可忽略)。
若搶購在進行中。此時業務代碼須要查詢商品在本次搶購中的庫存還有否(高頻請求,即圖中“争取名額”階段)。
“争搶名額”這塊。一會我們細講。先把時序圖說完。
若使用者搶到了名額。就同意使用者跳轉到第三方的支付頁面産生消費。(此時第三方笑了)。産生消費後,第三方自己的庫存-1,而且能夠實時、異步、完事對賬的方式通知我們。
2.2 怎樣保證商品庫的庫存可靠
此時,我們回想下檔案夾。“怎樣保證商品庫的庫存可靠”。
我們事實上是将商品庫的子庫前置在業務層抗壓力。那麼,怎樣保證大家的庫存情況穩定,不會由于搶購業務。導緻庫存波動影響使用者體驗。這裡就須要提一個業務RD須要關注的問題。須要做好取舍。
要麼,我們保證大家看到的庫存規律一緻,要麼,我們保證單個使用者看到的庫存規律一緻。若保證大家看到的庫存降低的規律一緻,且同一時刻庫存大家看到的庫存都一樣。
這就對系統有資料強一緻性要求。須要非常大成本,還僅僅能逐漸逼近此要求要求的效果。
而我們若選擇後者,僅保證單個使用者看到的庫存降低規律一緻,雖放棄了資料強一緻,但以更少的時間盡可能實作了最好的效果。是以,我們用到了使用者來排隊。若搶到名額了。在搶購庫中的庫存 –(減減)。這樣單使用者操作期間,能看到規律的降低。不會出現此事看剩10個,一會看還有11個的情況。這時我們說怎樣内部排隊,怎樣來控制“查詢商品在本次搶購中的庫存還有否(高頻請求)”這個高頻請求。
我們建構商品次元的cache。上圖中盡管說是“隊列”。我們能夠用redis的list來真正實作個隊列,也能夠通過 /–來實作。
如果商品A,營運配了20件。此事來了N多使用者的請求。業務代碼都會來查詢cache_prefix_a_id這個隊列的長度,若隊列長度≤0,則有權去–(減減)搶購庫的商品庫存。若隊列長度在20件内。則通過業務代碼内的等待來等待隊頭的位置。然後獲得搶購權限。若隊列長度太長。則能夠直接傳回,覺得商品已被搶空。
這時插入一個營運配庫的時序。便于大家了解。該時序圖有具體的說明和标注。就不展開了,例如以下圖:
此時。我們能夠想象,若上遊使用者的請求壓力是N,這個N會壓在業務層的搶購庫,俗話說“責任止于此”:P
2.3 怎樣和第三方多方對賬
那麼,我們回想檔案夾“怎樣和第三方多方對賬”?
這裡就要提到“Transaction Data”這個庫了。
Transaction ID為使用者次元的Session記錄,使用者從進入搶購業務開始。産生一個Transaction ID,該Transaction ID生命周期截止到使用者跳轉去第三方支付為止。期間在生活服務中産生的浏覽、搶購行為均會挂靠到該Transaction ID之下,并會在跳轉去第三方支付頁時攜帶該Transaction ID憑證。最基本的是須要記錄下:使用者獲得商品名額後,跳轉去第三方時,這一行為。
考慮到Transaction ID為搶購業務中,使用者操作行為的keyword段。值須要保證唯一。故此處能夠採用發号器之類的能力。
我們建構的Transaction Data記錄,就能夠依照DailyRun的方式,與第三方對賬,來fix雙方資料庫庫存不一緻等問題。
為什麼會産生我們和資源方的庫存不一緻,可能是由于使用者在第三方消費後。第三方callback我們時候失敗造成,也可能是由于使用者跳去第三方後并沒有真正支付。但我們的商品庫、搶購庫的庫存都已經降低造成的。原因可能有非常多,對賬機制是必要的。
3.項目總結
最後,我們回想回想設計,壓力問題在業務層攻克了,庫存不一緻問題我們通過對賬機制攻克了,産品的需求我們也通過旁路可配攻克了,嗯,能夠喝杯茶,發起評審。評審通過後開始寫代碼了。 :)
感謝大家。分享中的資料強一緻那塊。以及怎樣做取舍,都是非常有意思的點,都能夠展開聊非常久,這裡沒展開,大家能夠事後查查資料。
4.Q & A
Q1.1:請問。防刷是怎麼做的?一般搶購都有非常大優惠。
假設有人惡意刷,那正常的使用者就失去了購買的機會。比方,搶購的商品數為1000。有人惡意刷了900,那僅僅有 100 被正經常使使用者搶到。等惡意搶到的 900 經過後面的支付環節驗證後,可能已經過了搶購時間了。就算惡意搶到的 900 都支付成功。那對正經常使使用者也是不公平的。
A1.1:在這個業務場景中,我們做的是商品展示、商品的購買權的發放,真正産生消費是在第三方。
那麼,使用者刷的問題,須要我們和第三方支付頁面一起來控制。
在使用者通過排隊機制。獲得了購買名額後,跳轉去第三方時候,我們依照和第三方約定的加密方式傳遞加密資訊。第三方依照約定的解密方式解密成功後才同意使用者支付,加密解密的過程中能夠帶具有生命周期的内容。這樣。使用者在高頻請求支付頁面擷取商品時候,實際僅僅有:1)加密對;2)第一次。才可能獲得。隻是,第三方都是為了銷售出商品,是以這類合作的成功幾率不大。惡意刷,的确會在我們的業務層面展示商品沒量了。
導緻想買的使用者沒了機會。但上面Q1的回複,能夠保證第三方不受損。
對于你提到的這樣的刷的情況。若想在我們業務層規避,我想這就是一個通用的防SPAM的問題了。
這塊自己真懂得不多。
:P
Q1.2:要想準确的放刷。推斷的次元就多,邏輯就複雜;與之沖突的,搶購要求的是響應迅速。
A1.2:對的,@裴寶慶|SinoIOV ,搶購業務由于請求壓力大、熱門商品搶購并發高。切忌添加過多邏輯,切忌過多後端依賴。越簡單效果越好。
我們在設計系統時候,非常多事不是咱們一個系統能cover的。多少須要一些前置子產品、能力的建構ready後。我們的系統才幹run的不錯。還是建議 @裴寶慶|SinoIOV 快建構帳号體系、使用者消費記錄這兩部分。
Q2:對賬這裡,僅僅是和第三方去對照商品的庫存量嗎,金額這裡是否去對照?
A2:對賬,事實上是對照的消費資料。避免出現我們統計今日産生了X件商品共價值Y的消費,第三方給出的是消費了N件共M價值的消費。避免金額不一緻,造成結算、分成等問題的出現。我想你問題中的庫存量的diff問題,還得靠第三方定期的通過我們資料層的接口來update他們提供的商品。
事實上在我們的商品庫中,商品不一定僅僅同意第三方提供,也能夠同意第三方通過接口降低商品嘛。比方和一個賣水果的第三方合作,第三方上周公布說有100件,但這周線下熱銷,僅僅剩20件了。我們也應該同意第三方來update到一個低值。但這樣,我們的系統中就會複雜挺多。
Q3:防刷,避免第三方的推廣效果達不到問題。
A3:對的,使用者ID次元、IP次元。都是有效辦法。看詳細場景。有帳号體系的業務。用使用者ID次元效果最好。借助存儲記錄下每一個使用者的購買記錄,來控制就好。
市面上的電商站點,基本是搶購業務都須要登入,而且限制每件商品單人購買數量。事實上就是通過存儲記錄使用者的消費,而且再次産生消費前查詢并添加代碼邏輯來控制。
Q4:每次搶購活動的時候用一套新的驗證碼?
A4:驗證碼這個東東,屬于圖靈測試嘛,僅僅要測試方法好。而且盡可能保證每次産生的驗證資訊從未出現過且無規律,就是好的驗證碼啦。:)