基于lis3dh的简易倾角仪c源码_轻松应对并发问题,简易的火车票售票系统,Newbe.Claptrap 框架用例,第一步 — 业务分析...
Newbe.Claptrap 框架非常適合于解決具有并發(fā)問(wèn)題的業(yè)務(wù)系統(tǒng)?;疖?chē)票售票系統(tǒng),就是一個(gè)非常典型的場(chǎng)景用例。
本系列我們將逐步從業(yè)務(wù)、代碼、測(cè)試和部署多方面來(lái)介紹,如何使用 Newbe.Claptrap 框架來(lái)構(gòu)建一個(gè)簡(jiǎn)易的火車(chē)票售票系統(tǒng)。
吹牛先打草稿
讓我們來(lái)首先界定一個(gè)這個(gè)簡(jiǎn)易的火車(chē)售票系統(tǒng)所需要實(shí)現(xiàn)的業(yè)務(wù)邊界和系統(tǒng)性能要求。
業(yè)務(wù)邊界
該系統(tǒng)僅包含車(chē)票的余票管理部分。即查詢(xún)剩余座位,下單買(mǎi)票減座。
而生成訂單信息,付款,流量控制,請(qǐng)求風(fēng)控等等都不包含在本次討論的范圍中。
業(yè)務(wù)用例
- 查詢(xún)車(chē)票余票,能夠查詢(xún)兩個(gè)車(chē)站間可用的車(chē)次剩余座位數(shù)量
- 查詢(xún)車(chē)次對(duì)應(yīng)的車(chē)票余票,能夠查詢(xún)給定的車(chē)次,在各個(gè)車(chē)站之間還有多少剩余座位
- 支持選座下單,客戶(hù)能夠選擇給定的車(chē)次及座位,并下單買(mǎi)票
性能要求
- 查詢(xún)余票和下單購(gòu)票消耗平均不得超過(guò) 20ms。該時(shí)間僅包含服務(wù)端處理時(shí)間,即頁(yè)面網(wǎng)絡(luò)傳輸,頁(yè)面渲染等等不是框架關(guān)心的部分不計(jì)算在內(nèi)。
- 余票查詢(xún)可以存在延時(shí),但不是超過(guò) 5 秒鐘。延時(shí),即表示,可能查詢(xún)有票,但是下單無(wú)票的情況時(shí)被允許的。
難點(diǎn)分析
余票管理
火車(chē)票余票管理的難點(diǎn),其實(shí)就在于其余票庫(kù)存的特殊性。
普通的電商商品,以 SKU 為最小單位,每個(gè) SKU 之間相互獨(dú)立,互不影響。
例如:當(dāng)前我正在售賣(mài)原產(chǎn)自賽博坦星球的阿姆斯特朗回旋加速炮,紅色和白色兩款分別一萬(wàn)個(gè)。那么用戶(hù)在下單時(shí),只要分別控制紅色和白色兩款的庫(kù)存分別不超賣(mài)即可。他們之間沒(méi)有相互關(guān)系。
但是火車(chē)票余票,卻有所不同,因?yàn)橛嗥睍?huì)受到已賣(mài)票起終點(diǎn)而受到影響。下面結(jié)合一個(gè)簡(jiǎn)單的邏輯模型,來(lái)詳細(xì)的了解一下這種特殊性。
現(xiàn)在,我們假設(shè)存在一個(gè)車(chē)次,分別經(jīng)過(guò) a,b,c,d 四個(gè)站點(diǎn),同時(shí),我們簡(jiǎn)化場(chǎng)景,假設(shè)車(chē)次中只有一個(gè)座位。
那么在沒(méi)有任何人購(gòu)票之前,這個(gè)車(chē)次的余票情況就如下所示:
起終點(diǎn) 余票量
a,b 1
a,c 1
a,d 1
b,c 1
b,d 1
c,d 1
如果現(xiàn)在有一位客戶(hù)購(gòu)買(mǎi)了一張 a,c 的車(chē)票。那么由于只有一個(gè)座位,所以,a,b 和 b,c 的余票也就都沒(méi)有。余票情況就變成了如下所示:
起終點(diǎn) 余票量
a,b 0
a,c 0
a,d 1
b,c 0
b,d 1
c,d 1
更直白一點(diǎn),如果有一位客戶(hù)購(gòu)買(mǎi)了全程車(chē)票 a,d,那么所有的余票都將全部變?yōu)?0。因?yàn)檫@個(gè)座位上始終都坐著這位乘客。
這也就是火車(chē)票的特殊性:同一個(gè)車(chē)次的同一個(gè)座位,其各個(gè)起終點(diǎn)的余票數(shù)量,會(huì)受到已售出的車(chē)票的起終點(diǎn)的影響。
延伸一點(diǎn),很容易得出,同一車(chē)次的不同座位之間是沒(méi)有這種影響的。
余票查詢(xún)
正如上一節(jié)所述,由于余票庫(kù)存的特殊性。對(duì)于同一個(gè)車(chē)次 a,b,c,d,其可能的購(gòu)票選擇就有 6 種。
并且我們很容易就得出,選擇的種類(lèi)數(shù)的計(jì)算方法實(shí)際上就是在 n 個(gè)站點(diǎn)中選取 2 個(gè)的組合數(shù),即 c (n,2) 。
那么如果有一輛經(jīng)過(guò) 34 個(gè)站點(diǎn)的車(chē)次,其可能的組合就是 c (34,2) = 561 。
如何高效應(yīng)對(duì)可能存在的多種查詢(xún)也是該系統(tǒng)需要解決的問(wèn)題。
Claptrap 邏輯設(shè)計(jì)
Actor 模式是天生適合于解決并發(fā)問(wèn)題的設(shè)計(jì)模式。基于該理念之上的 Newbe.Claptrap 框架自然也能夠應(yīng)對(duì)以上提到的難點(diǎn)。
最小競(jìng)爭(zhēng)資源
類(lèi)比多線程編程中 “資源競(jìng)爭(zhēng)” 的概念,這里筆者提出在業(yè)務(wù)系統(tǒng)中的 “最小競(jìng)爭(zhēng)資源” 概念。借助這個(gè)概念可以很簡(jiǎn)單的找到如何應(yīng)用 Newbe.Claptrap 的設(shè)計(jì)點(diǎn)。
例如在筆者售賣(mài)阿布斯特朗回旋加速炮的例子中,同款顏色下的每個(gè)商品都是一個(gè) “最小競(jìng)爭(zhēng)資源”。
注意,這里不是說(shuō),同款顏色下的所有商品是一個(gè) “最小競(jìng)爭(zhēng)資源”。因?yàn)?#xff0c;如果對(duì)一萬(wàn)個(gè)商品進(jìn)行編號(hào),那么搶購(gòu)一號(hào)商品和二號(hào)商品,本身其實(shí)不存在競(jìng)爭(zhēng)關(guān)系。因此,每個(gè)商品都是一個(gè)最小競(jìng)爭(zhēng)資源。
那么在火車(chē)票余票的例子中,最小競(jìng)爭(zhēng)資源則是:同一車(chē)次上的同一個(gè)座位。
正如上面所述,同一車(chē)次上的同一座位,在選擇不同的起終點(diǎn)是,余票情況時(shí)存在競(jìng)爭(zhēng)關(guān)系的。具體一點(diǎn),比如筆者想購(gòu)買(mǎi) a,c 的車(chē)票,而讀者想買(mǎi) a,b 的車(chē)票。那么我們就有競(jìng)爭(zhēng)關(guān)系,我們只會(huì)有一個(gè)人能夠成功的購(gòu)買(mǎi)到這個(gè) “最小競(jìng)爭(zhēng)資源”。
這里有一些筆者認(rèn)為可用的例子:
- 在一個(gè)只允許單端登錄的業(yè)務(wù)系統(tǒng)中,一個(gè)用戶(hù)的登錄票據(jù)就是最小競(jìng)爭(zhēng)資源
- 在一個(gè)配置系統(tǒng)中,每個(gè)配置項(xiàng)都是最小競(jìng)爭(zhēng)資源
- 在一個(gè)股票交易市場(chǎng)中,每個(gè)買(mǎi)單或者賣(mài)單都是最小競(jìng)爭(zhēng)資源
最小競(jìng)爭(zhēng)資源 與 Claptrap
之所以要提及 “最小競(jìng)爭(zhēng)資源”,是因?yàn)樵谠O(shè)計(jì) Claptrap 的 State 時(shí),區(qū)別最小競(jìng)爭(zhēng)資源是對(duì)系統(tǒng)設(shè)計(jì)的一個(gè)重要依據(jù)。
這里列出一條筆者的結(jié)論:Claptrap 的 State 至少應(yīng)該大于等于 “最小競(jìng)爭(zhēng)資源” 的范圍。
結(jié)合阿布斯特朗回旋加速炮的例子,如果同款顏色的所有商品設(shè)計(jì)在同一個(gè) Claptrap 的 State 中(大于最小競(jìng)爭(zhēng)資源)。那么,不同用戶(hù)購(gòu)買(mǎi)商品就會(huì)相互影響,因?yàn)?#xff0c;Claptrap 基于的 Actor 模式是排隊(duì)處理請(qǐng)求的。也就是說(shuō),假設(shè)每個(gè)商品需要處理 10ms,那么最快也需要 10000 * 10 ms 來(lái)處理所有的購(gòu)買(mǎi)請(qǐng)求。但如果每個(gè)商品都進(jìn)行編號(hào),每個(gè)商品設(shè)計(jì)為單獨(dú)的 Claptrap 的 State。那么由于他們是互不相關(guān)的。賣(mài)掉所有商品,理論上就只需要 10ms。
也就是說(shuō):如果 Claptrap 的 State 大于最小競(jìng)爭(zhēng)資源的范圍,系統(tǒng)不會(huì)有正確性的問(wèn)題,但可能有一些性能損失。
在進(jìn)一步,前文提到在火車(chē)售票的例子中,同一車(chē)次上的同一個(gè)座位是最小競(jìng)爭(zhēng)資源,因此,我們可以將這個(gè)業(yè)務(wù)實(shí)體設(shè)計(jì)為 Claptrap 的 State 。但如果設(shè)計(jì)范圍比這個(gè)還小呢?
例如:我們將 Claptrap 的 State 設(shè)計(jì)為同一車(chē)次上同一座位在不同起終點(diǎn)的余票。那么,就會(huì)遇到一個(gè)很傳統(tǒng)的問(wèn)題:“如何確保分布式系統(tǒng)中數(shù)據(jù)的正確性”。對(duì)于這點(diǎn),筆者無(wú)法展開(kāi)來(lái)說(shuō),因?yàn)楣P者也說(shuō)不清楚,就只是草率的丟下一句結(jié)論:“如果 Claptrap 的 State 小于最小競(jìng)爭(zhēng)資源的范圍,Claptrap 間的關(guān)系將會(huì)變得難以處理,存在風(fēng)險(xiǎn)。”
Claptrap 主體設(shè)計(jì)
接下來(lái),結(jié)合上面所述的理論。我們直接丟出設(shè)計(jì)方案。
將同一車(chē)次上的每個(gè)座位都設(shè)計(jì)為一個(gè) Claptrap - SeatGrain
該 Claptrap 的 State 包含有一個(gè)基本信息
IList<int> Stations
途徑車(chē)站的 id 列表,開(kāi)頭為始發(fā)站,結(jié)尾為終點(diǎn)站。主要購(gòu)票時(shí)進(jìn)行驗(yàn)證。
Dictionary<int, int> StationDic
途徑車(chē)站 id 的索引反向字典。Stations 是 index-id 的列表,而該字典是對(duì)應(yīng)的 id-index 的字典,為了加快查詢(xún)。
List<string> RequestIds
關(guān)鍵屬性。每個(gè)區(qū)間上,已購(gòu)票的購(gòu)票 id。例如,index 為 0 ,即表示車(chē)站 0 到車(chē)站 1 的購(gòu)票 id。如果為空則表示暫無(wú)認(rèn)購(gòu)票。
有了這數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì),那么就可以來(lái)實(shí)現(xiàn)兩個(gè)業(yè)務(wù)了。
驗(yàn)證是否可以購(gòu)買(mǎi)
通過(guò)傳入兩個(gè)車(chē)站 id,可以查詢(xún)到這個(gè)作為是否屬于這個(gè) SeatGrain 。并且查詢(xún)到起終點(diǎn)對(duì)應(yīng)的所有區(qū)間段。只要判斷這個(gè)從 RequestIds 中判斷是否所有的區(qū)間段都沒(méi)有購(gòu)票 Id 即可。若都沒(méi)有,則說(shuō)明可以購(gòu)買(mǎi)。如果有任何一段上已有購(gòu)票 Id,則說(shuō)明已經(jīng)無(wú)法購(gòu)買(mǎi)了。
舉例來(lái)說(shuō),當(dāng)前 Stations 的情況是 10,11,12,13. 而 RequestIds 是 0,1,0。
那么,如果要購(gòu)買(mǎi) 10->12 的車(chē)票,則不行,因?yàn)?RequestIds 第二個(gè)區(qū)間已經(jīng)被購(gòu)買(mǎi)。
但是,如果要購(gòu)買(mǎi) 10->11 的車(chē)票,則可以,因?yàn)?RequestIds 第一個(gè)區(qū)間還無(wú)人購(gòu)買(mǎi)。
購(gòu)買(mǎi)
將起終點(diǎn)對(duì)應(yīng)在 RequestIds 中所有的區(qū)間段設(shè)置上購(gòu)票 Id 即可。
單元測(cè)試用例
可以通過(guò)以下鏈接來(lái)查看關(guān)于以上算法的代碼實(shí)現(xiàn):
- Github
- Gitee
將同一車(chē)次上的所有座位的余票情況設(shè)計(jì)為一個(gè) Claptrap - TrainGran
該 Claptrap 的 State 包含有一些基本信息
IReadOnlyList<int> Stations
途徑車(chē)站的 id 列表,開(kāi)頭為始發(fā)站,結(jié)尾為終點(diǎn)站。主查詢(xún)時(shí)進(jìn)行驗(yàn)證。
IDictionary<StationTuple, int> SeatCount
關(guān)鍵屬性。StationTuple 表示一個(gè)起終點(diǎn)。集合包含了所有可能的起終點(diǎn)的余票情況。例如,根據(jù)上文,如果該車(chē)次經(jīng)過(guò) 34 個(gè)地點(diǎn),則該字典包含有 561 個(gè)鍵值對(duì)
基于以上的數(shù)據(jù)結(jié)構(gòu),只需要在每次 SeatGrain 完成下單后,將對(duì)應(yīng)的信息同步到該 Grain 即可。
例如,假如 a,c 發(fā)生了一次購(gòu)票,則將 a,c /a,b /b,c 的余票都減一即可。
這里可以借助本框架內(nèi)置的 Minion 機(jī)制來(lái)實(shí)現(xiàn)。
值得一提的是,這是一個(gè)比 “最小競(jìng)爭(zhēng)資源” 大的設(shè)計(jì)。因?yàn)椴樵?xún)場(chǎng)景在該業(yè)務(wù)場(chǎng)景中不需要絕對(duì)的快速。這樣設(shè)計(jì)可以減少系統(tǒng)的復(fù)雜度。
小結(jié)
本篇,我們通過(guò)業(yè)務(wù)分析,得出了火車(chē)票余票管理和 Newbe.Claptrap 的結(jié)合點(diǎn)。
后續(xù)我們將圍繞本篇的設(shè)計(jì),說(shuō)明如何進(jìn)行開(kāi)發(fā)、測(cè)試和部署。
實(shí)際上,項(xiàng)目源碼已經(jīng)構(gòu)建完畢,讀者可以從以下地址獲取:
- Github
- Gitee
特別感謝 wangjunjx8868 采用 Blazor 為本樣例制作的界面。
最后但是最重要!
最近作者正在構(gòu)建以反應(yīng)式、Actor模式和事件溯源為理論基礎(chǔ)的一套服務(wù)端開(kāi)發(fā)框架。希望為開(kāi)發(fā)者提供能夠便于開(kāi)發(fā)出 “分布式”、“可水平擴(kuò)展”、“可測(cè)試性高” 的應(yīng)用系統(tǒng) ——Newbe.Claptrap
本篇文章是該框架的一篇技術(shù)選文,屬于技術(shù)構(gòu)成的一部分。如果讀者對(duì)該內(nèi)容感興趣,歡迎轉(zhuǎn)發(fā)、評(píng)論、收藏文章以及項(xiàng)目。您的支持是促進(jìn)項(xiàng)目成功的關(guān)鍵。
聯(lián)系方式:
- Github Issue
- Gitee Issue
- 公開(kāi)郵箱 newbe-claptrap@googlegroups.com (發(fā)送到該郵箱的內(nèi)容將被公開(kāi))
- Gitter
- QQ 群 553474855
您還可以查閱本系列的其他選文:
GitHub 項(xiàng)目地址:https://github.com/newbe36524/Newbe.Claptrap
Gitee 項(xiàng)目地址:https://gitee.com/yks/Newbe.Claptrap
您當(dāng)前查看的是先行發(fā)布于 www.newbe.pro 上的博客文章,實(shí)際開(kāi)發(fā)文檔隨版本而迭代。若要查看最新的開(kāi)發(fā)文檔,需要移步 http://claptrap.newbe.pro。- 本文作者: newbe36524
- 本文鏈接: https://www.newbe.pro/Newbe.Claptrap/Create-A-Train-Ticketing-System-In-Newbe-Claptrap-1/
- 版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請(qǐng)注明出處!
總結(jié)
以上是生活随笔為你收集整理的基于lis3dh的简易倾角仪c源码_轻松应对并发问题,简易的火车票售票系统,Newbe.Claptrap 框架用例,第一步 — 业务分析...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Syntax Error: Error:
- 下一篇: java信息管理系统总结_java实现科