自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

京東搶購(gòu)服務(wù)高并發(fā)實(shí)踐

開發(fā) 開發(fā)工具
限時(shí)搶購(gòu)又稱閃購(gòu),英文Flash sale,起源于法國(guó)網(wǎng)站Vente Privée。

服務(wù)介紹

限時(shí)搶購(gòu)又稱閃購(gòu),英文Flash sale,起源于法國(guó)網(wǎng)站Vente Privée。閃購(gòu)模式即是以互聯(lián)網(wǎng)為媒介的B2C電子零售交易活動(dòng),以限時(shí)特賣的形式,定期定時(shí)推出國(guó)際知名品牌的商品,一般以原價(jià)1-5折的價(jià)格供專屬會(huì)員限時(shí)搶購(gòu),每次特賣時(shí)間持續(xù)5-10天不等,先到先買,限時(shí)限量,售完即止。顧客在指定時(shí)間內(nèi)(一般為20分鐘)必須付款,否則商品會(huì)重新放到待銷售商品的行列里。

模式特征:

品牌豐富 —— 推出國(guó)內(nèi)外一二線名牌商品,供消費(fèi)者購(gòu)買選擇;

時(shí)間短暫 —— 每個(gè)品牌推出時(shí)間短暫,一般為5—10天,先到先買,限量售賣,售完即止;

折扣超低 —— 以商品原價(jià)1—5折的價(jià)格銷售,折扣力度大。

摘自【百度百科】,通過這段簡(jiǎn)介相信對(duì)限時(shí)搶購(gòu)有了一定的了解,我們內(nèi)部稱之為搶購(gòu)系統(tǒng)。

對(duì)于搶購(gòu)系統(tǒng)來說,首先要有可搶購(gòu)的活動(dòng),而且這些活動(dòng)具有促銷性質(zhì),比如直降500元。其次要求可搶購(gòu)的活動(dòng)類目豐富,用戶才有充分的選擇性。618(6.1-6.20)期間增量促銷活動(dòng)量非常多,可能某個(gè)活動(dòng)力度特別大,大多用戶都在搶,必然對(duì)系統(tǒng)是一個(gè)考驗(yàn)。這樣搶購(gòu)系統(tǒng)具有秒殺特性,并發(fā)訪問量高,同時(shí)用戶也可選購(gòu)多個(gè)限時(shí)搶商品,與普通商品一起進(jìn)購(gòu)物車結(jié)算。這種大型活動(dòng)的負(fù)載可能是平時(shí)的幾十倍,所以通過增加硬件、優(yōu)化瓶頸代碼等手段是很難達(dá)到目標(biāo)的,所以搶購(gòu)系統(tǒng)得專門設(shè)計(jì)。

服務(wù)主要功能

創(chuàng)建促銷服務(wù):采銷創(chuàng)建促銷后,促銷管理系統(tǒng)審核通過后,會(huì)調(diào)用搶購(gòu)系統(tǒng)創(chuàng)建促銷;

搶服務(wù):為符合條件的訂單操作剩余數(shù),主要是扣減剩余數(shù);

針對(duì)哪些SKU

目前主要為單品促銷,直降或者一口價(jià),比如:

主要渠道

移動(dòng)APP、微信、手Q和主站

限購(gòu)類型

限數(shù)量、限ip、限pin和限制ip與pin

系統(tǒng)設(shè)計(jì)要點(diǎn)

如何實(shí)現(xiàn)實(shí)時(shí)庫(kù)存?

這里說的庫(kù)存不是真正意義上的庫(kù)存,其實(shí)是該促銷可以搶購(gòu)的數(shù)量,真正的庫(kù)存在基礎(chǔ)庫(kù)存服務(wù)。用戶點(diǎn)擊『提交訂單』按鈕后,在搶購(gòu)系統(tǒng)中獲取了資格后才去基礎(chǔ)庫(kù)存服務(wù)中扣減真正的庫(kù)存;而搶購(gòu)系統(tǒng)控制的就是資格/剩余數(shù)。傳統(tǒng)方案利用數(shù)據(jù)庫(kù)行鎖,但是在促銷高峰數(shù)據(jù)庫(kù)壓力過大導(dǎo)致服務(wù)不可用,目前采用redis集群(16分片)緩存促銷信息,例如促銷id、促銷剩余數(shù)、搶次數(shù)等,搶的過程中按照促銷id散列到對(duì)應(yīng)分片,實(shí)時(shí)扣減剩余數(shù)。當(dāng)剩余數(shù)為0或促銷刪除,價(jià)格恢復(fù)原價(jià)。

如何設(shè)計(jì)搶購(gòu)redis數(shù)據(jù)結(jié)構(gòu)?

采銷人員發(fā)布促銷后,在搶購(gòu)redis中生成一筆記錄,給搶服務(wù)提供基本信息。每一個(gè)促銷對(duì)應(yīng)一個(gè)促銷id,促銷信息是Hashes結(jié)構(gòu)。

例如促銷A,對(duì)應(yīng)的類型為單品促銷,我們暫且認(rèn)為類型值為1,對(duì)應(yīng)redis中的key為 C_A_1,數(shù)據(jù)結(jié)構(gòu)內(nèi)容類似于如下:

  1. o: 100 // 原始數(shù)量  
  2. b: 99 // 可搶購(gòu)數(shù)量,假如搶購(gòu)了一個(gè)剩下了99  
  3. c: 1 // 搶購(gòu)次數(shù)記錄,用來限流,后面會(huì)介紹到 

如何保證不超賣?

因?yàn)榭蹨p資格是一組操作,我們利用EVAL操作redis剩余數(shù)實(shí)現(xiàn)原子化操作,偽代碼如下:

  1. local key = KEYS[1] 
  2. local tag  = "b" 
  3. local num   = tonumber(ARGV[1]); 
  4. local lastNum = redis.call('HINCRBY',key,tag,-num); 
  5. if業(yè)務(wù)性判斷ortonumber(lastNum) == 0then 
  6.    return lastNum 
  7. end 

如上代碼會(huì)返回剩余數(shù),如果小于等于0了,則沒有庫(kù)存了。

如何提高吞吐量?

減少網(wǎng)絡(luò)交互(一次搶數(shù)據(jù)通過 EVALSHA 一次性提交給redis集群);數(shù)據(jù)庫(kù)操作異步化(使用JMQ異步記錄日志)。

如何保證可用性?

采用JSF(京東內(nèi)部SOA框架)對(duì)外開放服務(wù)(搶服務(wù)和發(fā)布促銷服務(wù)),可降級(jí)為系統(tǒng)自身webservice服務(wù);

搶購(gòu)系統(tǒng)主要依賴于redis集群,redis采用一主三從集群方案,部署在兩個(gè)機(jī)房,每個(gè)集群16個(gè)分片,每?jī)煞制灿靡慌_(tái)物理機(jī),可通過配置中心切換主從;

如果Redis掛掉了,如何恢復(fù)呢?通過匯總MySQL中的搶購(gòu)和取消流水日志,并恢復(fù)Redis的搶購(gòu)數(shù)量。

系統(tǒng)架構(gòu)

這里主要涉及搶服務(wù)架構(gòu)剖析,因?yàn)樗哂械湫偷母卟l(fā)特性,下面是基本架構(gòu)概圖:

注:此處的庫(kù)存是可搶購(gòu)數(shù)量設(shè)置,或者叫做資格/剩余數(shù),并非真正的實(shí)際庫(kù)存。

搶服務(wù)流程

Redis使用單個(gè)Lua解釋器去運(yùn)行所有腳本,并且Redis 也保證腳本會(huì)以原子性(atomic)的方式執(zhí)行:當(dāng)某個(gè)腳本正在運(yùn)行的時(shí)候,不會(huì)有其他腳本或Redis命令被執(zhí)行。這種特性很好的解決了搶服務(wù)流程中并發(fā)帶來的問題。

REDIS+LUA搶購(gòu)子流程:

此流程通過lua Script腳本實(shí)現(xiàn),我們暫時(shí)命名為q.lua(主要功能限流和扣減促銷活動(dòng)剩余數(shù))。這樣把搶購(gòu)流程與Script腳本結(jié)合,一次性提交給Redis減少網(wǎng)絡(luò)交互,使得性能大大提升。

q.lua偽代碼:

  1. --[[ 
  2. --!@brief 促銷Id下限流:可以防止某個(gè)促銷過熱導(dǎo)致服務(wù)不可以用 
  3. --]] 
  4. local function limited() 
  5.     -- todo: 實(shí)現(xiàn) 
  6. end 
  7. --[[ 
  8. --!@brief 限制邏輯(ip和pin):比如有的促銷是限制ip,這里校驗(yàn)ip是否存在,如果為限ip類型搶購(gòu)活動(dòng),存在拋出異常告知ip已經(jīng)存在不能搶購(gòu) 
  9. --]] 
  10. local function check_ip_pin() 
  11.     -- todo: 實(shí)現(xiàn) 
  12. end 
  13. --[[ 
  14. --!@brief 記錄訂單號(hào):主要目的實(shí)現(xiàn)搶方法冪等性,調(diào)用方網(wǎng)絡(luò)超時(shí)可以重復(fù)調(diào)用,存在訂單號(hào)直接返回?fù)屬?gòu)成功,不至于超賣 
  15. --]] 
  16. local function record_order_id() 
  17.     -- todo: 實(shí)現(xiàn) 
  18. end 
  19. --[[ 
  20. --!@brief 扣減剩余數(shù) 
  21. --]] 
  22. local function scalebuy() 
  23.     -- 
  24.     local lastNum = redis.call('HINCRBY',key,tag,-num); 
  25.     -- 
  26. end 
  27.   
  28. -- 調(diào)用順序不可調(diào)整 
  29. -- 1 限流 
  30. local status,msg = limited() 
  31. if status == 0then 
  32.     return msg 
  33. end 
  34. -- 2 校驗(yàn) 
  35. status,msg = check_ip_pin() 
  36. if status == 0 then 
  37.     return msg 
  38. end 
  39. -- 3 記錄訂單 
  40. status,msg = record_order_id() 
  41. if status == 0 then 
  42.     return msg 
  43. end 
  44. -- 4 扣減剩余數(shù) 
  45. status,msg = scalebuy() 
  46. if status == 0 then 
  47.     return msg 
  48. end 
  49. -- 5 返回成功標(biāo)示 
  50. return 1 

子流程具體如下:

1、解析請(qǐng)求參數(shù),根據(jù)促銷Id按照J(rèn)edis中MurmurHash算法獲取分片,然后按照分片包裝Pipeline批量發(fā)送請(qǐng)求參數(shù)argList;

2、獲取系統(tǒng)初始化時(shí)SCRIPT LOAD加載q.lua返回的串shaValue;

3、執(zhí)行EVALSHA,偽代碼如下:

  1. // 其他操作 
  2. Pipeline p; 
  3. // 初始化p 
  4. p.evalsha(shaValue,keyList, argList); 
  5. // 其他操作 

4、處理返回結(jié)果,只要有一個(gè)分片失敗,本次搶購(gòu)就失敗。

補(bǔ)充:詳細(xì)Script操作可以參考Jedis中 ScriptingCommandsTest。

JMQ發(fā)送子流程:

執(zhí)行REDIS+LUA搶購(gòu)子流程成功僅僅代表著操作redis成功,發(fā)送jmq(京東mq基礎(chǔ)服務(wù))成功(后端異步將實(shí)時(shí)庫(kù)存更新到MySQL)才算一筆搶購(gòu)成功,否則算搶購(gòu)失敗。這么設(shè)計(jì)的原因主要是保證搶購(gòu)redis和mysql記錄最終一致,發(fā)送失敗需要回滾REDIS+LUA搶購(gòu)子流程(恢復(fù)Redis的庫(kù)存和搶購(gòu)資格)。當(dāng)然要考慮降級(jí),jmq不可用時(shí),直接切到j(luò)sf服務(wù)模擬jmq,也就是直接寫MySQL庫(kù),前提是限流次數(shù)調(diào)小,否則數(shù)據(jù)庫(kù)有壓力過大的風(fēng)險(xiǎn)。這樣雖然用戶體驗(yàn)下降了,但是服務(wù)依然可用。開關(guān)都在配置中心操作,一分鐘內(nèi)生效。

資格回滾子流程:

發(fā)送JMQ失敗必須回滾,否則就出現(xiàn)了超賣現(xiàn)象,具體流程同REDIS+LUA搶購(gòu)子流程類似,是它的逆向流程,只不過運(yùn)行腳本不同罷了。

限流處理

方法級(jí)限流,限流閾值通過配置中心配置,一分鐘生效,偽代碼如下:

  1. private static AtomicInteger atomic = new AtomicInteger(0); 
  2. public void test() { 
  3.     try { 
  4.          // 限流 
  5.         int limitNum = XXX.getLimitNum(); 
  6.         int nowConcurrent = atomic.incrementAndGet(); 
  7.         if(nowConcurrent > limitNum) { 
  8.             // 異常處理 
  9.         }   
  10.         // 正常業(yè)務(wù)邏輯 
  11.     } catch(Exception e) { 
  12.         // 異常處理 
  13.     } finally { 
  14.         atomic.decrementAndGet(); 
  15.     } 

q.lua中促銷級(jí)別的限流,主要利用C_A_1中c的搶次數(shù)和閾值比對(duì)。比如促銷A,60秒內(nèi)只能搶60000次,超過閾值60000該促銷就會(huì)搶購(gòu)失敗。

到此搶購(gòu)系統(tǒng)的核心邏輯就介紹完了,這里邊還有一些細(xì)節(jié)問題需要大家在設(shè)計(jì)時(shí)思考,如限購(gòu)(如每個(gè)人限購(gòu)2個(gè))、真實(shí)庫(kù)存不足取消、用戶取消訂單歸還資格、Redis掛了恢復(fù)數(shù)據(jù)、停促銷(時(shí)間過期停、庫(kù)存不足停)等等。

作者:張子良,京東高級(jí)開發(fā)工程師,在京東負(fù)責(zé)搶購(gòu)后端服務(wù)系統(tǒng)架構(gòu)和開發(fā)工作。

【本文來自51CTO專欄作者張開濤的微信公眾號(hào)(開濤的博客),公眾號(hào)id: kaitao-1234567】

 

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來源: 開濤的博客
相關(guān)推薦

2022-03-18 09:11:56

高并發(fā)搶購(gòu)系統(tǒng)架構(gòu)

2019-03-18 05:02:30

高并發(fā)京東架構(gòu)

2021-10-28 09:36:12

高并發(fā)數(shù)據(jù)實(shí)踐

2022-03-26 08:47:57

高并發(fā)架構(gòu)設(shè)計(jì)緩存擊穿

2019-12-24 09:30:59

蘇寧高可用高并發(fā)

2017-12-28 09:41:29

微服務(wù)網(wǎng)關(guān)容錯(cuò)

2018-02-05 09:30:23

高性能高并發(fā)服務(wù)

2020-07-29 07:28:14

分布式限流系統(tǒng)

2016-11-28 09:58:53

京東服務(wù)閉環(huán)實(shí)踐

2020-10-14 15:53:45

秒殺秒殺系統(tǒng)流量

2012-10-23 18:54:39

索尼SW125投影機(jī)

2022-05-17 11:46:48

高并發(fā)服務(wù)數(shù)據(jù)庫(kù)

2018-09-12 10:21:12

價(jià)格保護(hù)高并緩存

2015-06-23 11:20:12

京東618

2021-05-13 21:58:00

高并發(fā)應(yīng)用Asyncio

2022-10-28 17:35:57

架構(gòu)網(wǎng)絡(luò)拓?fù)?/a>

2018-05-19 18:24:02

WOT2018微服務(wù)容器

2022-10-24 00:04:57

飛天茅臺(tái)架構(gòu)搶購(gòu)

2019-06-28 10:55:04

預(yù)熱高并發(fā)并發(fā)高

2018-02-27 14:30:17

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)