亚洲av中文无码乱人伦在线视色,网曝黑料国产吃瓜,无码国产精品久久一区免费,亚洲av在在线观看,亚洲av国产午夜精品一区二区

馬上雙十一,教你用Python實(shí)現(xiàn)秒殺系統(tǒng)(python搶秒殺)

馬上雙十一,教你用Python實(shí)現(xiàn)秒殺系統(tǒng)(python搶秒殺)

堅(jiān)持學(xué)習(xí)很難,養(yǎng)成學(xué)習(xí)習(xí)慣更難

架構(gòu)搭建是重點(diǎn),代碼或語言實(shí)現(xiàn)較簡(jiǎn)單。

本篇用python redis rabbitmq搭建一個(gè)秒殺系統(tǒng)。 用flask編寫后端,只包含秒殺相關(guān)程序,省略具體的業(yè)務(wù)接口。.

關(guān)注,轉(zhuǎn)發(fā),私信小編“01”即可免費(fèi)領(lǐng)取python學(xué)習(xí)資料!

馬上雙十一,教你用Python實(shí)現(xiàn)秒殺系統(tǒng)(python搶秒殺)

項(xiàng)目會(huì)持續(xù)更新,完整代碼見github: https://github.com/Sssmeb/seckilling

(如果覺得有幫助的話可以點(diǎn)個(gè)star~~~~ )

篇幅有限,不會(huì)介紹redis或rabbitmq的基本操作。 如果沒學(xué)過相關(guān)知識(shí)的只需先了解以下兩點(diǎn),也可以看懂本架構(gòu)。

  1. redis是內(nèi)存型數(shù)據(jù)庫(kù),讀寫速度遠(yuǎn)快于mysql這類磁盤型數(shù)據(jù)庫(kù),常用來作緩存。
  2. rabbitmq消息隊(duì)列,可以理解為生產(chǎn)者消費(fèi)者模型,用隊(duì)列來存儲(chǔ)任務(wù),生產(chǎn)與消費(fèi)解耦。

前言

在介紹架構(gòu)之前,我們需要先知道秒殺系統(tǒng)面臨的難點(diǎn)是什么。

首先在普通的系統(tǒng)中, 最大的瓶頸是在于底層的數(shù)據(jù)庫(kù)端 。 因?yàn)榈讓訑?shù)據(jù)庫(kù)(比如常見的mysql)是磁盤存儲(chǔ)的,所以讀寫IO較慢,而且連接數(shù)有限。

而在秒殺業(yè)務(wù)場(chǎng)景,最大的特點(diǎn)是 瞬時(shí)的高并發(fā) ,即在短時(shí)間內(nèi)會(huì)有大量的請(qǐng)求到來。 如果讓所有請(qǐng)求都打到底層數(shù)據(jù)庫(kù)上,很大可能數(shù)據(jù)庫(kù)會(huì)直接崩掉,即使數(shù)據(jù)庫(kù)能承受住大量的連接請(qǐng)求,但大量的請(qǐng)求讀寫都會(huì)導(dǎo)致大量的鎖沖突,導(dǎo)致響應(yīng)速度大大減慢。 而響應(yīng)速度對(duì)于用戶體驗(yàn)來說,無疑是十分重要的。

所以在這里,需要明確第一個(gè)目標(biāo): 讓盡可能少、盡可能有效的請(qǐng)求打到底層數(shù)據(jù)庫(kù) 。

當(dāng)我們回頭再考慮這個(gè)業(yè)務(wù)場(chǎng)景,其實(shí)絕大部分的請(qǐng)求都不應(yīng)該打到底層數(shù)據(jù)庫(kù)。 因?yàn)橐话闵唐穾?kù)存可能只有搶購(gòu)用戶數(shù)的百分之一,甚至更少。 所以我們需要一些機(jī)制、策略,提前將無效的請(qǐng)求返回。

而站在整個(gè)網(wǎng)站設(shè)計(jì)的角度,第二個(gè)目標(biāo): 越上層越容易實(shí)現(xiàn),越有效。

這里的層指:

  1. 頁(yè)面層
  2. 網(wǎng)絡(luò)層
  3. 應(yīng)用層
  4. 服務(wù)層
  5. 數(shù)據(jù)層

例如在前端頁(yè)面層,如果不做處理,用戶在點(diǎn)擊搶購(gòu)按鈕以后,見網(wǎng)頁(yè)沒有響應(yīng),可能會(huì)再點(diǎn)擊3-4次甚至更多,這樣可能會(huì)導(dǎo)致最終有80%的請(qǐng)求都是重復(fù)無效的。 但只需要前端在設(shè)計(jì)時(shí),將點(diǎn)擊后的按鈕置灰,防止用戶多次點(diǎn)擊發(fā)送請(qǐng)求。 即簡(jiǎn)單又有效。

以下簡(jiǎn)單指出各層可實(shí)施的策略:

  1. 頁(yè)面層(簡(jiǎn)單的實(shí)現(xiàn)可以屏蔽 90%的請(qǐng)求)
  2. 按鈕置灰,禁止用戶重復(fù)提交
  3. 驗(yàn)證碼
  4. 網(wǎng)絡(luò)層
  5. 通過ip限制一定時(shí)間內(nèi)的請(qǐng)求次數(shù)
  6. 應(yīng)用層
  7. 一個(gè)頁(yè)面最占用資源、帶寬的是cs js 圖片等靜態(tài)資源
  8. 避免所有請(qǐng)求都到服務(wù)器的硬盤上取
  9. 動(dòng)靜分離,壓縮緩存處理(CDN nginx
  10. 根據(jù)uid限頻,頁(yè)面緩存技術(shù)(web服務(wù)器 nginx)
  11. 反向代理 負(fù)載均衡 (nginx)
  12. 服務(wù)層
  13. 微服務(wù)
  14. redis
  15. 消息隊(duì)列 削峰 異步處理
  16. 數(shù)據(jù)層
  17. 讀寫分離
  18. 分庫(kù)分表
  19. 集群

每一層具體實(shí)現(xiàn)起來都是一個(gè)很大的架構(gòu),這里我們主要專注于服務(wù)層,使用redis 消息隊(duì)列。

基礎(chǔ)架構(gòu)

馬上雙十一,教你用Python實(shí)現(xiàn)秒殺系統(tǒng)(python搶秒殺)

架構(gòu).png

核心: 服務(wù)異步拆分,減少耦合,使用緩存,加快響應(yīng)。

避免同步 的請(qǐng)求執(zhí)行,如: 請(qǐng)求→訂單→支付→修改庫(kù)存→結(jié)束返回,這種模型在高并發(fā)場(chǎng)景下,阻塞多,響應(yīng)慢,服務(wù)器壓力大,不可取。

這里實(shí)現(xiàn)的架構(gòu)是: 1. 請(qǐng)求→返回 2. 支付→返回 3. 修改庫(kù)存

這種服務(wù)拆分歸功于 消息隊(duì)列。 核心思想是,將接收到的請(qǐng)求 存儲(chǔ)到隊(duì)列中就可以響應(yīng)用戶了,后端在隊(duì)列中取出請(qǐng)求再做后續(xù)操作即可。 簡(jiǎn)單理解就是,我們將請(qǐng)求記錄下來,晚點(diǎn)空閑了再處理。

基礎(chǔ)數(shù)據(jù)存儲(chǔ)

數(shù)據(jù)、請(qǐng)求的存儲(chǔ)情況如:

  1. mysql中存儲(chǔ)商品信息、訂單信息
  2. redis存入商品信息、設(shè)置計(jì)數(shù)器、存儲(chǔ)成功訂單的數(shù)據(jù)結(jié)構(gòu)等
  3. rabbitmq創(chuàng)建隊(duì)列
  • 訂單隊(duì)列(用戶提交請(qǐng)求)
  • 延遲隊(duì)列(訂單必須在15分鐘內(nèi)支付)
  • 成交隊(duì)列(訂單支付成功,等待寫入數(shù)據(jù)庫(kù))

流程

以下所有代碼都是截取核心部分,完整代碼參看

訂單請(qǐng)求

redis計(jì)數(shù)器

假設(shè)我們只有100件商品庫(kù)存,但可能會(huì)收到10萬條搶購(gòu)請(qǐng)求。 也就是會(huì)有將近9.9萬條無效的請(qǐng)求,所以我們要將這些請(qǐng)求阻隔。

最簡(jiǎn)單的方法,也是我們使用的方法: 實(shí)現(xiàn)一個(gè)count變量,每個(gè)請(qǐng)求進(jìn)入都加一,當(dāng)count大于100時(shí)則直接返回失敗即可 。

這里我們使用redis也是因?yàn)閮?nèi)存讀寫速度要遠(yuǎn)大于類似mysql的磁盤讀寫。

代碼實(shí)現(xiàn)增加了分布式鎖。相關(guān)知識(shí)可以看:https://www.jianshu.com/p/cf311cfb1689

訂單隊(duì)列

異步拆分服務(wù)的關(guān)鍵。 為了提高響應(yīng)速度,我們只需要 將請(qǐng)求訂單任務(wù)保存下來 (消息隊(duì)列),就可以 直接返回用戶 了。 而 不需要將請(qǐng)求轉(zhuǎn)到后端做大量的判斷、處理、數(shù)據(jù)庫(kù)讀寫操作后才返回用戶 。 所有可以 大大的加快響應(yīng)速度 。 后端可以隨時(shí)從隊(duì)列中取出請(qǐng)求再做各自處理,即使等搶購(gòu)活動(dòng)結(jié)束再進(jìn)行底層數(shù)據(jù)庫(kù)讀寫也沒有問題。

所以核心思路就是把請(qǐng)求放入隊(duì)列,然后直接返回用戶即可。

# 計(jì)數(shù)器 1flag = plus_counter(goods_id)# 成功申請(qǐng)if flag:# 生成唯一的訂單號(hào)order_id = uuid.uuid1()# 訂單信息(也是請(qǐng)求任務(wù)信息)order_info = {“goods_id” : goods_id,”user_id” : user_id,”order_id” : str(order_id)}try :# 進(jìn)入訂單隊(duì)列enter_order_queue(order_info)res[ “status” ] = Trueres[ “msg” ] = “搶購(gòu)成功,請(qǐng)?jiān)?5分鐘之內(nèi)付款!”res[ “order_id” ] = str(order_id)return jsonify(res)except Exception as e:print( “log: ” , e)res[ “status” ] = Falseres[ “msg” ] = “搶購(gòu)出錯(cuò),請(qǐng)重試.” str(e)returnjsonify(res), 202

enter_order_queue是將訂單請(qǐng)求(訂單信息),也就是order_info發(fā)送到對(duì)應(yīng)的隊(duì)列。 與之對(duì)應(yīng)的消費(fèi)者,只需要將該訂單信息寫入數(shù)據(jù)庫(kù)對(duì)應(yīng)的訂單表即可。

注意: 此時(shí)訂單還沒支付,所以數(shù)據(jù)庫(kù)表中可以設(shè)置一個(gè)status字段,標(biāo)識(shí)訂單的狀態(tài)。

唯一標(biāo)識(shí)

不局限于uuid,可用毫秒時(shí)間戳之類的唯一標(biāo)識(shí)。

可以看到上面代碼中,我們利用uuid生成了一個(gè)唯一標(biāo)識(shí)作為訂單號(hào),并且返回給用戶。

主要的作用是:

  1. 標(biāo)識(shí)訂單。因?yàn)橛唵握?qǐng)求僅僅只是被我們?nèi)腙?duì)列,消費(fèi)者可能還沒開始處理。(即訂單可能還未被創(chuàng)建在數(shù)據(jù)庫(kù)中)
  2. 返回給用戶,可用于后續(xù)的支付操作。

當(dāng)用戶支付時(shí)需要校驗(yàn)用戶與對(duì)應(yīng)的單號(hào)是否正確,這里我們?nèi)杂胷edis,以提高查詢速度。

所以在上面的基礎(chǔ)上,我們需要加多一步,將訂單信息寫入redis。

order_info = {“goods_id” : goods_id,”user_id” : user_id,”order_id” : str(order_id)}try :# 在redis中創(chuàng)建這個(gè)訂單create_order(order_info)enter_order_queue(order_info)res[ “status” ] = Trueres[ “msg” ] = “搶購(gòu)成功,請(qǐng)?jiān)?5分鐘之內(nèi)付款!”res[ “order_id” ] = str(order_id)returnjsonify(res)訂單的結(jié)構(gòu)這里采用字典,提高檢索效率。 插入如:redis_conn.hset( “order:” str(goods_id), str(order_id), str(user_id))超時(shí)隊(duì)列

正如前面所見,我們提示用戶在15分鐘之內(nèi)支付,符合日常業(yè)務(wù)場(chǎng)景。

在消息隊(duì)列中有延遲隊(duì)列的應(yīng)用,符合我們的超時(shí)需求。 所以我們同樣用消息隊(duì)列來實(shí)現(xiàn)這一業(yè)務(wù)需求。 即我們?cè)趧?chuàng)建訂單時(shí),同樣將訂單信息傳入隊(duì)列中。

try :

# redis保存訂單信息

create_order(order_info)

# 訂單隊(duì)列

enter_order_queue(order_info)

# 超時(shí)隊(duì)列

enter_overtime_queue(order_info)

最終,當(dāng)一個(gè)訂單請(qǐng)求通過計(jì)數(shù)器后,需要經(jīng)歷的三個(gè)過程如上。 無論是redis或是rabbitmq消息隊(duì)列,都是內(nèi)存操作,速度都是足夠快的。 不需要經(jīng)過數(shù)據(jù)層即可響應(yīng)用戶。

至此,一個(gè)訂單“創(chuàng)建”完成。

支付請(qǐng)求

訂單請(qǐng)求完成后,用戶會(huì)獲得訂單號(hào)。 用戶必須在15分鐘內(nèi)完成支付操作。 在執(zhí)行支付時(shí)需要考慮:

  1. 檢查用戶和對(duì)應(yīng)的訂單號(hào)是否正確
  • create_order(order_info) 時(shí),我們已將訂單信息寫入redis??蓮倪@里取得數(shù)據(jù)做校驗(yàn)
  1. 檢查訂單是否超時(shí)
  • 如果我們?cè)O(shè)置的超時(shí)隊(duì)列超過指定時(shí)間,則隊(duì)列里的請(qǐng)求會(huì)被處理(消費(fèi))
  • 我們只需要將超時(shí)的單號(hào)寫入redis即可做校驗(yàn)
  1. 支付成功入成交隊(duì)列
  • 同理于訂單隊(duì)列。只需將成交的訂單信息寫入消息隊(duì)列中,后續(xù)系統(tǒng)空閑時(shí)再寫入數(shù)據(jù)庫(kù)即可。
  • 也是為了提高用戶響應(yīng)速度,用戶不需要等待數(shù)據(jù)庫(kù)io完成后才收到結(jié)果。

代碼流程為:

但訂單通過檢查、并支付完成后。 我們還需要將成交的訂單寫入redis,記錄狀態(tài)(用于其他判斷)。 再將訂單請(qǐng)求寫入隊(duì)列即可返回。 全程內(nèi)存操作,速度快,帶來了快響應(yīng)。 之后,我們可以等搶購(gòu)活動(dòng)結(jié)束后,系統(tǒng)比較空閑的時(shí)間將訂單同步到底層數(shù)據(jù)庫(kù),同步數(shù)據(jù)。

總覽

所以兩個(gè)核心的操作是:

  1. 通過rabbitmq消息隊(duì)列異步拆分服務(wù),加快了響應(yīng)的速度
  2. 通過redis內(nèi)存讀寫,減少操作時(shí)間

再總結(jié)整個(gè)框架:

  1. 用戶提交訂單
  • 通過redis計(jì)數(shù)器篩選
  • 成功則返回標(biāo)識(shí),然后入訂單隊(duì)列 超時(shí)隊(duì)列
    • 標(biāo)識(shí)與用戶信息寫入redis,用于后續(xù)驗(yàn)證支付
    • 訂單隊(duì)列,mysql監(jiān)聽,寫入mysql的訂單歷史表
    • 超時(shí)訂單隊(duì)列有計(jì)時(shí)功能,一定時(shí)間內(nèi)未支付,訂單失效,搶購(gòu)失敗。寫入redis(標(biāo)志失?。?/li>
  • 失敗直接返回
  • 訂單服務(wù)結(jié)束
  1. 用戶支付訂單
  • 驗(yàn)證訂單以及檢查是否已超時(shí)(是否已在redis相關(guān)結(jié)構(gòu)內(nèi))
  • 成功支付則入支付隊(duì)列
    • mysql監(jiān)聽這個(gè)隊(duì)列,執(zhí)行庫(kù)存同步操作。
    • 寫入redis
  • 失敗或超時(shí)直接返回
  • 支付服務(wù)結(jié)束

馬上雙十一,教你用Python實(shí)現(xiàn)秒殺系統(tǒng)(python搶秒殺)

流程

注意

  1. 代碼持續(xù)更新,完整代碼: https://github.com/Sssmeb/seckilling (覺得有幫助的可以給個(gè)star)
  2. 本架構(gòu)只專注于服務(wù)層的業(yè)務(wù)架構(gòu),有很多沒有涉及的點(diǎn)(高可用,數(shù)據(jù)一致性等),一個(gè)完整的搶購(gòu)系統(tǒng)是一個(gè)非常龐大的。
  3. 文中沒有介紹mysql數(shù)據(jù)層相關(guān)的操作,一方面是為了提示大家,在高并發(fā)的情景下應(yīng)該盡可能的避免這類的磁盤io操作。 另一方面,mysql數(shù)據(jù)層相關(guān)操作是在消息隊(duì)列 消費(fèi)者進(jìn)行操作的,這里不詳解操作。 只注重整體架構(gòu)。 具體操作見代碼。

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號(hào)
公眾號(hào)
在線咨詢
返回頂部