版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、淺談12306核心模型設(shè)計(jì)思路和架構(gòu)設(shè)計(jì)前言春節(jié)期間,無意中看到一篇文章, 文章中講到12306的業(yè)務(wù)復(fù)雜度遠(yuǎn)遠(yuǎn)比淘寶天貓這種電商網(wǎng)站要復(fù)雜。后來自己想想,也確實(shí)如此。所以,很想挑戰(zhàn)一下12306這個(gè)系統(tǒng)的核心領(lǐng)域模型的 設(shè)計(jì)。一般的電商網(wǎng)站,購(gòu)買都是基于商品的概念,每個(gè)商品有一定量的庫(kù)存,用戶的購(gòu)買行為是針對(duì)商品的。當(dāng)用戶發(fā)起購(gòu)買行為時(shí),系統(tǒng)只需要生成訂單并對(duì)用 戶要購(gòu)買的商品減庫(kù)存即可。但是,12306就不是那么簡(jiǎn)單了,具體復(fù)雜在哪里,我下面會(huì)進(jìn)一步分析。另外一個(gè)讓我寫這篇文章的原因,是我發(fā)現(xiàn)也許是否是因?yàn)槟壳?2306的核心領(lǐng)域模型設(shè)計(jì)的不夠好,導(dǎo)致用戶購(gòu)票時(shí)要處理的業(yè)務(wù)邏輯異常復(fù)雜,維
2、護(hù) 數(shù)據(jù)一致性的難度也幾百倍的上升,同時(shí)面對(duì)高并發(fā)的訂票也難以支持很高的TPS。我覺得,越是復(fù)雜的業(yè)務(wù),就越要重視業(yè)務(wù)分析,重視領(lǐng)域模型的抽象和設(shè) 計(jì)。如果不假思索,憑以往經(jīng)驗(yàn)行事,則很可能會(huì)被以往的設(shè)計(jì)經(jīng)驗(yàn)先入為主,陷入死胡同。我發(fā)現(xiàn)技術(shù)人員往往更注重技術(shù)層面的解決方案,比如一上來就分析如 何集群、如何負(fù)載均衡、 如何排隊(duì)、如何分庫(kù)分表、如何用鎖,如何用緩存等技術(shù)問題,而忽略了最根本的業(yè)務(wù)層面的思考,如分析業(yè)務(wù)、領(lǐng)域建模。我認(rèn)為越是復(fù)雜的業(yè)務(wù)系統(tǒng),則越要設(shè) 計(jì)一個(gè)健壯的領(lǐng)域模型。如果一個(gè)系統(tǒng)的架構(gòu)我們?cè)O(shè)計(jì)錯(cuò)了,還有補(bǔ)救的余地,因?yàn)榧軜?gòu)最終沉淀的只是代碼,調(diào)整架構(gòu)即可(一個(gè)系統(tǒng)的架構(gòu)本身就是不
3、斷演進(jìn) 的);而如果領(lǐng)域模型設(shè)計(jì)錯(cuò)了,那要補(bǔ)救的代價(jià)是非常大的,因?yàn)轭I(lǐng)域模型沉淀的是數(shù)據(jù)結(jié)構(gòu)及其對(duì)應(yīng)的大量數(shù)據(jù),對(duì)任何一個(gè)大型系統(tǒng),要改核心領(lǐng)域模型都是 成本非常高的。本文的重點(diǎn)不是在如何解決高并發(fā)的問題,而是希望從業(yè)務(wù)角度去分析,12306的理想模型應(yīng)該是怎么樣的。網(wǎng)上目前談12306的文章貌似都是千篇一律的只談技術(shù),不談業(yè)務(wù)分析和如何建模的。所以我想寫一下自己的設(shè)計(jì)和大家交流學(xué)習(xí)。需求概述12306這個(gè)系統(tǒng),核心要解決的問題是網(wǎng)上售票。涉及到2個(gè)角色使用該系統(tǒng):用戶、鐵道部。用戶的核心訴求是查詢余票、購(gòu)票;鐵道部的核心訴求是 售票。購(gòu)票和售票其實(shí)是一個(gè)場(chǎng)景,對(duì)用戶來說是購(gòu)票,對(duì)鐵道部來說是
4、售票。因此,我們要設(shè)計(jì)一個(gè)在線的網(wǎng)站系統(tǒng),解決用戶的查詢余票、購(gòu)票,以及鐵道部的 售票這3個(gè)核心訴求。看起來,這3個(gè)場(chǎng)景都是圍繞火車票展開的。查詢余票:用戶輸入出發(fā)地、目的地、出發(fā)日三個(gè)條件,查詢可能存在的車次,用戶可以看到每個(gè)車次經(jīng)過的站點(diǎn)名稱,以及每種座位的余票數(shù)量。購(gòu)票:購(gòu)票分為訂票和付款兩個(gè)階段,本文重點(diǎn)分析訂票的模型設(shè)計(jì)和實(shí)現(xiàn)思路。其實(shí)還有很多其他的需求,比如給不同的車次設(shè)定銷售座位數(shù)配額,以及不同的區(qū)段設(shè)置不同的限額。但相比前面兩個(gè)需求來說,我覺得這個(gè)需求相對(duì)次要一些。需求分析確實(shí),12306也是一個(gè)電商系統(tǒng),而且看起來商品就是票了。因?yàn)槿绻岩粡埰笨闯墒且粋€(gè)商品,那購(gòu)票就類似于購(gòu)
5、買商品,然后每張票都有庫(kù)存,商品 也有庫(kù)存的概念。但是如果我們仔細(xì)想想,會(huì)發(fā)現(xiàn)12306要復(fù)雜很多,因?yàn)槲覀儫o法預(yù)先確定好所有的票,如果非要確定,那只能通過窮舉法了。我們以北京西到深圳北的G71車次高鐵為例(這里只考慮南下的方向,不考慮深圳北到北京西的,那是另外一個(gè)車次,叫G72),它有17個(gè)站(北京西 是01號(hào)站,深圳北是17號(hào)站),3種座位(商務(wù)、一等、二等)。表面看起來,這不就是3個(gè)商品嗎?G71商務(wù)座、G71一等座、G71二等座。大部分輕 易噴12306的技術(shù)人員(包括某些中等規(guī)模公司的專家、CTO)就是在這里栽第一個(gè)跟頭的。實(shí)際上,G71有136*3=408種商品(408個(gè) SKU)
6、,怎么算來的?如下:如果賣北京西始發(fā)的,有16種賣法(因?yàn)楹竺嬗?6個(gè)站),北京西到:保定、石家莊、鄭州、武漢、長(zhǎng)沙、廣州、虎門、深圳。都是一個(gè)獨(dú)立的商 品,同理,石家莊上車的,有15種下車的可能,以此類推,單以上下車的站來計(jì)算,有136種票:16+15+14.+2+1=136。每種票都有3種座 位,一共是408個(gè)商品。為了方便后面的討論,我們先明確一下票是什么?一張票的核心信息包括:出發(fā)時(shí)間、出發(fā)地、目的地、車次、座位號(hào)。持有票的人就擁有了一個(gè)憑證,該憑證表示持有它的人可以坐某個(gè)車次的某個(gè)座位號(hào), 從某地到某地。所以,一張票,對(duì)用戶來說是一個(gè)憑證,對(duì)鐵道部來說是一個(gè)承諾;那對(duì)系統(tǒng)來說是什么呢
7、?不知道。這就是我們要分析業(yè)務(wù),領(lǐng)域建模的原因,我 們?cè)倮^續(xù)思考吧。明白了票的核心信息后,我們?cè)倏纯碐71這個(gè)車次的高鐵,可以賣多少?gòu)埰保坑懻撉跋日f明一下,一輛火車的物理座位數(shù)(站票也可以看成是一種座位,因?yàn)檎酒币灿袛?shù)量配額)不等于可用的最大配合。所有的物理座位不可能都通過 12306網(wǎng)站來銷售,而是只會(huì)銷售一部分,比如40%。其余的還是會(huì)通過線下的方式銷售。不僅如此,可能有些站點(diǎn)上車的人會(huì)比較多,有些比較少,所以我 們還會(huì)給不同的區(qū)間配置不同的限額。比如D31 北京南至上海共有765張,北京南有260張,楊柳青有80張,泰安有76張。如果楊柳青的80張票售完就會(huì)顯示無票,就算其他站有票也會(huì)顯
8、示無票的。每 個(gè)車次肯定會(huì)有各種座位的配額和限額的配置的,這種配置我目前無法預(yù)料,但我已經(jīng)把這些規(guī)則都封裝近車次聚合根里了,所有的配置策略都是基于座位類型、站 點(diǎn)、區(qū)間配置的。關(guān)于票的配置抽象出來,我覺得主要有3種:1)某個(gè)區(qū)段最多允許出多少?gòu)垼?)某個(gè)區(qū)段最少允許出多少?gòu)垼?)某個(gè)站點(diǎn)上車的最多多少 張;當(dāng)用戶訂票時(shí),把用戶指定的區(qū)段和這3種配置條件進(jìn)行比較,3個(gè)條件都滿足,則可以出票。不滿足,則認(rèn)為無票了。下面舉個(gè)例子:ABCDEFG,這是所有站點(diǎn)。座位總配額是100,假設(shè)B站點(diǎn)上車,E站下車的人比較少,那我們就可以設(shè)定BE這個(gè)區(qū)段最多只能出10張票。所 以,只要是用戶的訂票是在這個(gè)區(qū)段內(nèi)
9、的,就最多出10張。再比如,一列車次,總共100個(gè)座位配額,希望全程票最少滿足80張,那我們只要給AG這個(gè)區(qū)段 設(shè)定最少80張。那任何訂票請(qǐng)求,如果是子區(qū)間的,就不能超過100-80,即20張。這兩種條件必須同時(shí)滿足,才允許出票。但是,不管如何做配額和限額,我們總是針對(duì)某個(gè)車次進(jìn)行配置,這些配置只是車次內(nèi)部售票時(shí)的一些額外的判斷條件(業(yè)務(wù)規(guī)則),不影響車次模型的核心 地位和對(duì)外暴露的功能。所以,為了本文討論的清楚起見,我后續(xù)的討論都不涉及配額和限額的問題,而是認(rèn)為任何區(qū)段都可以享受火車最大的物理座位數(shù)。并且,為了討論問題方便,我們減少一些站點(diǎn)來討論。假設(shè)某個(gè)車次有A,B,C,D四個(gè)站點(diǎn)。那00
10、1這個(gè)人購(gòu)買了A,B這個(gè)區(qū)間,系統(tǒng)會(huì)分配給 001一個(gè)座位x;但是因?yàn)?01坐到B站點(diǎn)后會(huì)下車,所以相當(dāng)于x這個(gè)座位又空出來了,也就是說,從B站點(diǎn)開始,系統(tǒng)又可以認(rèn)為x這個(gè)座位是可用的。所 以,我們得出結(jié)論:同一個(gè)座位,其實(shí)可以同時(shí)出售AB,BC這兩張票。通過這個(gè)簡(jiǎn)單的分析,我們知道,一列火車雖然只有有限的座位數(shù),比如1000個(gè)座 位。但可以賣出的票遠(yuǎn)遠(yuǎn)不止1000個(gè)。還是以A,B,C,D四個(gè)站點(diǎn)為例,假如火車總共有1000個(gè)座位,那AB可以賣1000張,BC也可以賣 1000張,同樣,CD也可以賣1000張。也就是說,理論上最多可以賣出3000張票。但是如果換一種賣法,所有人都是買ABCD的
11、票,也就是說所有的 票都是經(jīng)過所有站點(diǎn)的,那就是最多只能賣出1000張票了。而實(shí)際的場(chǎng)景,一定是介于1000到3000之間。然后實(shí)際的G71這個(gè)車次,有17個(gè)站,那 到底可以賣出多少個(gè)票,大家應(yīng)該可以算了吧。理論上這17個(gè)站中的任意兩個(gè)站點(diǎn)之間所形成的線段,都可以出售為一張票。我數(shù)學(xué)不好,算不太清楚,麻煩有數(shù) 學(xué)好的人幫我算算,呵呵。通過上面的分析,我們知道一張票的本質(zhì)是某個(gè)車次的某一段區(qū)間(一條線段),這個(gè)區(qū)間包含了若干個(gè)站點(diǎn)。然后我們還發(fā)現(xiàn),只要區(qū)間不重疊,那座位就不會(huì)發(fā)生競(jìng)爭(zhēng),可以被回收利用,也就是說,可以同時(shí)預(yù)先出售。另外,經(jīng)過更深入的分析,我們還發(fā)現(xiàn)區(qū)間有4種關(guān)系:1)不重疊;2)部
12、分重疊;3)完全重疊;4)覆蓋;不重疊的情況我們已經(jīng)討論過了,而覆蓋也 是重疊的一種。所以我們發(fā)現(xiàn)如果重疊,比如有兩個(gè)區(qū)間發(fā)生重疊,那重疊部分的區(qū)間(可能夸一個(gè)或多個(gè)站點(diǎn))是在爭(zhēng)搶座位的。因?yàn)榧僭O(shè)一列火車有100個(gè)座 位,那每個(gè)原子區(qū)間(兩個(gè)相鄰站點(diǎn)的連線),最多允許重疊99次。所以,經(jīng)過上面的分析,我們知道了一個(gè)車次能夠出售一張車票的核心業(yè)務(wù)規(guī)則是什么?就是:這張車票所包含的每個(gè)原子區(qū)間的重疊次數(shù)加1都不能超過車次的總座位數(shù),實(shí)際上重疊次數(shù)+1也可以理解為線段的厚度。模型設(shè)計(jì)上面我分析了一下票的本質(zhì)是什么。那接下來我們?cè)賮砜纯丛趺丛O(shè)計(jì)模型,來快速實(shí)現(xiàn)購(gòu)票的需求,重點(diǎn)是怎么設(shè)計(jì)商品聚合以及減庫(kù)
13、存的邏輯。傳統(tǒng)電商的思路如果按照普通電商的思路,把票(站點(diǎn)區(qū)間)設(shè)計(jì)為商品(聚合根),然后為票設(shè)計(jì)庫(kù)存數(shù)量。我個(gè)人覺得是很糟糕的。因?yàn)橐环矫孢@種聚合根非常多(上面 的G71就有408個(gè));另一方面,即便枚舉出來了,一次購(gòu)票也一定會(huì)影響非常多其他聚合根的庫(kù)存數(shù)量(只要被部分或全部重疊的區(qū)間都受影響)。這樣的一 次訂單處理的復(fù)雜度是難以評(píng)估的。而且這么多聚合根的更新要在一個(gè)事務(wù)里,這不是為難數(shù)據(jù)庫(kù)嗎?而且,這種設(shè)計(jì)必然帶來大量的事務(wù)的并發(fā)沖突,很可能導(dǎo)致 數(shù)據(jù)庫(kù)死鎖??傊艺J(rèn)為這種是典型的由于領(lǐng)域模型的設(shè)計(jì)錯(cuò)誤,導(dǎo)致并發(fā)沖突高、數(shù)據(jù)持久化落地困難?;蛘呷绻鉀Q并發(fā)問題,只能排隊(duì)單線程處理,但是
14、 仍然解決不了要在一個(gè)事務(wù)里修改大量聚合根的尷尬局面。聽說12306是采用了Pivotal Gemfire這種高大上的內(nèi)存數(shù)據(jù)庫(kù),我對(duì)這個(gè)不太了解。我不可想象要是不使用內(nèi)存數(shù)據(jù)庫(kù),他們要怎么實(shí)現(xiàn)車次內(nèi)的票之間的數(shù)據(jù)強(qiáng)一致性(就是保證所有 出售的票都是符合上面討論的業(yè)務(wù)規(guī)則的)?所以,這種設(shè)計(jì),我個(gè)人認(rèn)為是思維定勢(shì)了,把火車票看成是普通電商的商品來看待。所以,我們有時(shí)做設(shè)計(jì)又要依賴 于經(jīng)驗(yàn),又要不能被以往經(jīng)驗(yàn)所束縛,真的不容易,關(guān)鍵還是要根據(jù)具體的業(yè)務(wù)場(chǎng)景多多深入分析,盡量分析抽象出問題的本質(zhì)出來,這樣才能對(duì)癥下藥。那是否有 其他的設(shè)計(jì)思路呢?我的思路聚合設(shè)計(jì)通過上面的分析我們知道,其實(shí)任何一次
15、購(gòu)票都是針對(duì)某個(gè)車次的,我認(rèn)為車次是負(fù)責(zé)處理訂票的聚合根。我們看看一 個(gè)車次包含了哪些信息?一個(gè)車次包括了:1)車次名稱,如G71;2)座位數(shù),實(shí)際座位數(shù)會(huì)分類型,比如商務(wù)座20個(gè),一等座200個(gè);二等座500個(gè); 我們這里為了簡(jiǎn)化問題,可以暫時(shí)忽略類型,我認(rèn)為這個(gè)類型不影響核心的模型的設(shè)計(jì)決策。需要格外注意的是:這里的座位數(shù)不要理解為真實(shí)的物理座位數(shù),很有 可能比真實(shí)的座位數(shù)要少。因?yàn)槲覀儾豢赡馨岩粋€(gè)車次的所有座位都在網(wǎng)上通過12306來出售,而是只出售一部分,具體出售多少,要由工作人員人工指定。 3)經(jīng)過的站點(diǎn)信息(包括站點(diǎn)的ID、站點(diǎn)名稱等),注意:車次還會(huì)記錄這些站點(diǎn)之間的順序關(guān)系;4
16、)出發(fā)時(shí)間;看過GRASP九大模式中的信息專家模式的同學(xué)應(yīng)該知道,將職責(zé)分配給擁有執(zhí)行該職責(zé)所需信息的類。我們這個(gè)場(chǎng)景,車次具有一次出票的所有信息,所以我們應(yīng)該把出票的職責(zé)交給車次。另外學(xué)過DDD的同學(xué)應(yīng)該知道,聚合設(shè)計(jì)有一個(gè)原則,就是:聚合內(nèi)強(qiáng)一致性,聚合之間最終一致性。經(jīng) 過上面的分析,我們知道要產(chǎn)生一張票,其實(shí)要影響很多和這個(gè)票對(duì)應(yīng)的線段相交的其他票的可用數(shù)量。因?yàn)樗械恼军c(diǎn)信息都在車次聚合內(nèi)部,所以車次聚合內(nèi)部 自然可以維護(hù)所有的原子區(qū)間,以及每個(gè)原子區(qū)間的可用票數(shù)(相當(dāng)于是庫(kù)存數(shù))。當(dāng)一個(gè)原子區(qū)間的可用票數(shù)為0的時(shí)候,意味著火車針對(duì)這個(gè)區(qū)間的票已經(jīng)賣完 了。所以,我們完全可以讓車次這
17、個(gè)聚合根來保證出票時(shí)對(duì)所有原子區(qū)間的可用票數(shù)的更新的強(qiáng)一致性。對(duì)于車次聚合根來說,這很簡(jiǎn)單,因?yàn)橹皇菐状魏?jiǎn)單的內(nèi)存 操作而已,耗時(shí)可以忽略。一列火車假如有ABCD四個(gè)站點(diǎn),那原子區(qū)間就是3個(gè)。對(duì)于G71,則是16個(gè)。怎么判斷是否能出票基于上面的聚合設(shè)計(jì),出票時(shí)扣減庫(kù)存的邏輯是:根據(jù)訂單信息,拿到出發(fā)地和目的地,然后獲取這段區(qū)間里的所有的原子區(qū)間。然后嘗試將每個(gè)原子區(qū)間的可用票數(shù)減1,如果所有的原子區(qū)間都?jí)驕p,則購(gòu) 票成功;否則購(gòu)票失敗,提示用戶該票已經(jīng)賣完了。是不是很簡(jiǎn)單呢?知道了出票的邏輯,那退票的邏輯也就很簡(jiǎn)單了,就是把這個(gè)票的所有原子區(qū)間的可用票數(shù)加 1就OK了。如果我們從線段的厚度的角
18、度去考慮,那出票時(shí),每個(gè)原子區(qū)間的厚度就是+1,退票時(shí)就是減一。就是相反的操作,但本質(zhì)是一樣的。所以,通過這樣的思路,我們將一次訂票的處理控制在了一個(gè)聚合根里,用聚合根內(nèi)的強(qiáng)一致性的特性保證了訂票處理的強(qiáng)一致性,同時(shí)也保證了性能,免去 了并發(fā)沖突的可能性。傳統(tǒng)電商那種把票單做類似商品的核心聚合根的設(shè)計(jì),我當(dāng)時(shí)第一眼看到就覺得不妥。因?yàn)檫@違背了DDD強(qiáng)調(diào)的強(qiáng)一致性應(yīng)該由聚合根來保 證、聚合根之間的最終一致性通過Saga來保證的原則。還有一個(gè)很重要的概念我想說一下我的看法,就是座位和區(qū)間的關(guān)系。因?yàn)橛行┡笥押臀抑v,考慮座位號(hào)的問題,雖然都能減1,座位號(hào)也必須是同一個(gè)。我 覺得座位是全局共享的,和區(qū)
19、段無關(guān)(也許我的理解完全有誤,請(qǐng)大家指正)。座位是一個(gè)物理概念,一個(gè)用戶成功購(gòu)買了一張票后,座位就會(huì)少一個(gè),一張票唯一 對(duì)應(yīng)一個(gè)座位,但是一個(gè)座位有可能會(huì)對(duì)應(yīng)多張票;而區(qū)間是一個(gè)邏輯上的概念,區(qū)間的作用有兩個(gè):1)表示票的出發(fā)地和目的地;2)記錄票的可用數(shù)額。如果 區(qū)間能連通(即該區(qū)間內(nèi)的每個(gè)原子區(qū)間的可用數(shù)額都大于0),則表示允許擁有一個(gè)座位。所以,我覺得座位和票(區(qū)間)是兩個(gè)維度的概念。如何為票分配座位我覺得車次聚合根內(nèi)部應(yīng)該維護(hù)所有該車次已經(jīng)售出的票,已經(jīng)出售的票的的本質(zhì)是區(qū)間和座位的對(duì)應(yīng)關(guān)系。系統(tǒng)處理訂票時(shí),用戶提交過來的是一段區(qū)間。所以,系統(tǒng)應(yīng)該做兩個(gè)事情:1. 先根據(jù)區(qū)間去判斷是否
20、有可用的座位;2. 如果有可用座位,則再通過算法去選擇一個(gè)可用的座位;當(dāng)?shù)玫揭粋€(gè)可用座位后,就可以生成一張票了,然后保存這個(gè)票到車次聚合根內(nèi)部即可。下面舉個(gè)例子:假設(shè)現(xiàn)在的情況是座位有3個(gè),站點(diǎn)有4個(gè)座位:1,2,3站點(diǎn):abcd票的賣法1:票1:ab,1票2:bc,2票3:cd,3票4:ac,3票5:bd,1這種選座位的方式應(yīng)該比較高效,因?yàn)榭偸莾?yōu)先從座位池里去拿座位,只有在萬不得已的時(shí)候才會(huì)去回收可重復(fù)利用的票。上面的4,5兩個(gè)票,就是考慮回收利用的結(jié)果。票的賣法2:票1:ab,1票2:bc,1票3:cd,1票4:ac,2票5:bd,3這種選座位的方式應(yīng)該相對(duì)低效,因?yàn)榭偸莾?yōu)先會(huì)去掃描是否
21、有可回收的座位,而掃描相對(duì)直接從座位池里去拿票總是成本相對(duì)要高的。上面的2,3兩個(gè)票,就是考慮回收利用的結(jié)果。但是,優(yōu)先從座位池里拿票的算法有缺陷,就是會(huì)出現(xiàn)雖然第一步判斷認(rèn)為有可用的座位,但是這個(gè)座位可能不是全程都是同一個(gè)座位。舉例:假設(shè)現(xiàn)在的情況是座位有3個(gè),站點(diǎn)有4個(gè)座位:1,2,3站點(diǎn):abcd票的賣法3:票1:ab,1票2:bc,2票3:cd,3現(xiàn)在如果有人要買ad的票,那可用的座位有2,或者3。但是無論是2還是3,都要這個(gè)乘客中途換車位。比如賣給他座位2,那他ab是坐的座位2,但是bc的時(shí)候要坐座位1的。否則拿票2的那個(gè)人上車時(shí),發(fā)現(xiàn)座位2已經(jīng)有人了。而通過優(yōu)先回收利用的算法,是沒
22、這個(gè)問題的。所以,從上面的分析我們也知道選座位的算法該怎么寫了,就是采用優(yōu)先回收利用座位的算法。我認(rèn)為不管我們這里怎么設(shè)計(jì)算法,都不影響大局,因?yàn)檫@一切都只發(fā)生在車次聚合根內(nèi)部,這就是預(yù)先設(shè)計(jì)好聚合根,明確出票職責(zé)在哪個(gè)對(duì)象上的好處。模型分析總結(jié)1. 我認(rèn)為票不是核心聚合根,票只是一次出票的結(jié)果,一個(gè)憑證而已。2. 12306真正的核心聚合根應(yīng)該是車次,車次具有出票的職責(zé),一次出票具體做的事情有:1. 判斷是否可出票;2. 選擇可用的座位;3. 更新一次出票時(shí)所有原子區(qū)間的可用票數(shù),用于判斷下次是否能出票;4. 維護(hù)所有已售出的票,用于為選擇可用座位提供依據(jù);通過這樣的模型設(shè)計(jì),我們可以確保一
23、次出票處理只會(huì)在一個(gè)車次聚合根內(nèi)進(jìn)行。這樣的好處是:1. 不需要依賴數(shù)據(jù)庫(kù)事務(wù)就能實(shí)現(xiàn)數(shù)據(jù)修改的強(qiáng)一致性,因?yàn)樗行薷闹辉谝粋€(gè)聚合根內(nèi)發(fā)生;2. 在保證數(shù)據(jù)強(qiáng)一致性的同時(shí)還能提供很高的并發(fā)處理能力,具體設(shè)計(jì)見下面的架構(gòu)設(shè)計(jì);架構(gòu)設(shè)計(jì)(非本文重點(diǎn),沒興趣的朋友可以略過)我覺得12306這樣的業(yè)務(wù)場(chǎng)景,非常適合使用CQRS架構(gòu);因?yàn)槭紫人且粋€(gè)查多寫少、但是寫的業(yè)務(wù)邏輯非常復(fù)雜的系統(tǒng)。所以,非常適合做架構(gòu)層面的讀寫分離,即采用CQRS架構(gòu)。而且應(yīng)該使用數(shù)據(jù)存儲(chǔ)也分離的CQRS。這樣CQ兩端才可以完全不需要顧及對(duì)方的問題,各自優(yōu)化自己的問題即可。我們可以在C端使用DDD領(lǐng)域模型的思路,用良好設(shè)計(jì)的領(lǐng)
24、域模型實(shí)現(xiàn)復(fù)雜的業(yè)務(wù)規(guī)則和業(yè)務(wù)邏輯。而Q端則使用分布式緩存方案,實(shí)現(xiàn)可伸縮的查詢能力。訂票的實(shí)現(xiàn)思路同時(shí)借助像ENode這樣的框架,我們可以實(shí)現(xiàn)in-memory + Event Sourcing的架構(gòu)。Event Sourcing技術(shù),可以讓領(lǐng)域模型的所有狀態(tài)修改的持久化統(tǒng)一起來,本來要用ORM的方式保存聚合根最新狀態(tài)的,現(xiàn)在只需要簡(jiǎn)單的通用的方式保存一個(gè) 事件即可(一次訂票只涉及一個(gè)車次聚合根的修改,修改只產(chǎn)生一個(gè)事件,只需要持久化一個(gè)事件(一個(gè)JSON串)即可,保證了高性能,無須依賴事務(wù),而且通 過ENode可以解決并發(fā)問題)。我們只要保存了聚合根每次變化的事件(事件的結(jié)構(gòu)怎么設(shè)計(jì),本文
25、不做多的介紹了,大家可以思考下),就相當(dāng)于保存了聚合 根的最新狀態(tài)。而正是由于Event Sourcing技術(shù)的引入,讓我們的模型可以一直存活在內(nèi)存中,即可以使用in-memory技術(shù)。不要小看in-memory技術(shù),in- memory技術(shù)在某些方面對(duì)提高命令的處理性能非常有幫助。比如就以我們車次聚合根處理出票的邏輯,假設(shè)某個(gè)車次有大量的命令發(fā)送到分布式消息隊(duì)列,然 后有一臺(tái)機(jī)器訂閱了這個(gè)隊(duì)列的消息,然后這臺(tái)機(jī)器處理這個(gè)車次的訂票命令時(shí),由于這個(gè)車次聚合根一直在內(nèi)存,所以就省去了每次要去數(shù)據(jù)庫(kù)取出聚合根的步 驟,相當(dāng)于少了一次數(shù)據(jù)庫(kù)IO。這樣的好處是,因?yàn)橐粋€(gè)車次能夠真正出售的票是有限的,因
26、為座位就那么幾個(gè),比如就1000個(gè)座位,估計(jì)一般正常情況也就 出個(gè)2000個(gè)左右的票吧(具體能出多少?gòu)埰币Q于區(qū)間的相交程度,上面分析過)。也就是說,這個(gè)聚合根只會(huì)產(chǎn)生2000個(gè)事件,也就是說只會(huì)有 2000個(gè)訂票命令的處理是會(huì)產(chǎn)生事件,并持久化事件;而其余的大量命令,因?yàn)檐嚧卧趦?nèi)存計(jì)算后發(fā)現(xiàn)沒有余票了,就不會(huì)做任何修改,也不會(huì)產(chǎn)生領(lǐng)域事件, 這樣就可以直接處理下一個(gè)訂票命令了。這樣就可以大大提高處理訂票命令的性能。另外一個(gè)問題我覺得還需要提一下,因?yàn)橛脩粲喥背晒?,還需要付款。但用戶有可能不去付款或者沒有在規(guī)定的時(shí)間內(nèi)完成付款。那這種情況下,系統(tǒng)會(huì)自 動(dòng)釋放該用戶之前訂購(gòu)的票。所以基于這樣
27、的需求,我們?cè)跇I(yè)務(wù)上需要支持業(yè)務(wù)級(jí)別的2pc。即先預(yù)扣庫(kù)存,也就是先占住這張票一定時(shí)間(比如15分鐘),然 后付款成功后再真實(shí)給你這張票,系統(tǒng)做真正的庫(kù)存修改。通過這樣的預(yù)扣處理,可以保證不會(huì)出現(xiàn)超賣的情況。這個(gè)思路其實(shí)和傳統(tǒng)電商比如淘寶這樣的系統(tǒng)類 似,我就不多展開了,我之前寫的Conference案例也是這樣的思路,大家有興趣的可以去看一下我之前錄制的視頻。查詢余票的實(shí)現(xiàn)思路我覺得余票的查詢的實(shí)現(xiàn)相對(duì)簡(jiǎn)單。雖然對(duì)于12306來說,查詢的請(qǐng)求占了80%,提交訂單的請(qǐng)求只占20%。但查詢由于對(duì)數(shù)據(jù)沒有修改,所以我們 完全可以使用分布式緩存來實(shí)現(xiàn)。我們只需要精心設(shè)計(jì)好緩存的key即可;緩存key
28、的多少要看成本,如果所有可能的查詢都設(shè)計(jì)對(duì)應(yīng)的key,那時(shí)間復(fù)雜度 為1,查詢性能自然高;但代價(jià)也大,因?yàn)閗ey多了。如果想key少一點(diǎn),那查詢的復(fù)雜度自然要上去一點(diǎn)。所以緩存設(shè)計(jì)無非就是空間換時(shí)間的思路。然后, 緩存的更新無非就是:自動(dòng)失效、定時(shí)更新、主動(dòng)通知3種。通過CQRS架構(gòu),由于CQ兩端是事件驅(qū)動(dòng)的,當(dāng)C端有任何狀態(tài)變化,都會(huì)產(chǎn)生對(duì)應(yīng)的事件去通知 Q端,所以我們幾乎可以做到Q端的準(zhǔn)實(shí)時(shí)更新。同時(shí)由于CQ兩端的完全解耦,Q端我們可以設(shè)計(jì)多種存儲(chǔ),如數(shù)據(jù)庫(kù)和緩存(Redis等);數(shù)據(jù)庫(kù)用于線下維護(hù)關(guān)系型數(shù)據(jù),緩存用戶實(shí)時(shí)查詢。數(shù)據(jù) 庫(kù)和緩存的更新速度相互不受影響,因?yàn)槭遣⑿械摹?duì)同一個(gè)事件,可以10臺(tái)機(jī)器負(fù)責(zé)更新緩存,100臺(tái)機(jī)器負(fù)責(zé)更新數(shù)據(jù)庫(kù)。即便數(shù)據(jù)庫(kù)的更新很慢,也不會(huì) 影響緩存的更新進(jìn)度。這就是CQRS架構(gòu)的好處,CQ的架構(gòu)完全不同,且我們隨時(shí)可以重建一種新的Q端存儲(chǔ)。不知道大家體會(huì)到了沒有?關(guān)于緩存key的設(shè)計(jì),我覺得主要從查
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 高端醫(yī)療器械研發(fā)項(xiàng)目知識(shí)產(chǎn)權(quán)免責(zé)協(xié)議
- 跨境電商交易風(fēng)險(xiǎn)規(guī)避協(xié)議
- 電子郵件托管與發(fā)送服務(wù)合同
- 合作開發(fā)協(xié)議
- 健身鍛煉活動(dòng)免責(zé)協(xié)議書
- 2025年度高端裝備制造項(xiàng)目融資合同范本
- 2025年度生物制藥技術(shù)培訓(xùn)保密及研發(fā)合同3篇
- 2025年度企事業(yè)單位保安人員勞務(wù)派遣服務(wù)合同3篇
- 2025年度膨潤(rùn)土原材料供應(yīng)鏈管理合同4篇
- 2025年度土地房產(chǎn)轉(zhuǎn)讓與稅收籌劃專項(xiàng)服務(wù)合同
- 豪邁CutRite V9板材優(yōu)化軟件學(xué)習(xí)教材
- 臨床三基考試題庫(kù)(附答案)
- 醫(yī)學(xué)課件三叉神經(jīng)痛3
- 2024年全國(guó)職業(yè)院校技能大賽高職組(智能節(jié)水系統(tǒng)設(shè)計(jì)與安裝賽項(xiàng))考試題庫(kù)-上(單選題)
- 鷓鴣山隧道瓦斯地段專項(xiàng)施工方案
- HG∕T 2058.1-2016 搪玻璃溫度計(jì)套
- 九宮數(shù)獨(dú)200題(附答案全)
- 泌尿科一科一品匯報(bào)課件
- 國(guó)家電網(wǎng)有限公司架空輸電線路帶電作業(yè)工作管理規(guī)定
- 白銅錫電鍍工藝
- 拜耳法氧化鋁生產(chǎn)工藝
評(píng)論
0/150
提交評(píng)論