導(dǎo)讀:價格中心系統(tǒng)是途牛網(wǎng)眾多系統(tǒng)中很重要的一個,幾乎所有的售賣價格計(jì)算都由此系統(tǒng)產(chǎn)生。初期由于缺乏合理設(shè)計(jì),無法及時滿足業(yè)務(wù)高速增長的需求,出現(xiàn)價格計(jì)算速度慢、系統(tǒng)不穩(wěn)定、增加功能困難等問題,系統(tǒng)面臨巨大壓力,在這種情況下我們啟動了系統(tǒng)優(yōu)化。經(jīng)過優(yōu)化系統(tǒng)的穩(wěn)定性得到巨大提升,特殊場景下的性能提升為原來的幾十倍,平均性能提升為原來的幾倍。本文將就途牛價格中心系統(tǒng)的優(yōu)化實(shí)踐作深入分享。
一、旅游產(chǎn)品的特點(diǎn)
旅游產(chǎn)品是各種資源的整合,包括機(jī)票、酒店、門票、簽證等所有旅行過程中使用到的各種服務(wù)。如圖1,以三亞五日游這個產(chǎn)品為例,第一天飛到三亞并入住酒店,然后各種玩,第五天飛走,其中涉及到很多種服務(wù)。
圖1 三亞五日游
在計(jì)算過程中需要處理這些場景:
產(chǎn)品的每個團(tuán)期都需要排列選出最低價格的資源組成;
同一資源在不同的團(tuán)期下價格可能不同(淡旺季,不同供應(yīng)商報價不同,資源庫存影響);
需要考慮同行程下的酒店連住天數(shù),選出連住多天總價最便宜的酒店資源;
某些資源只有計(jì)算時通過多個條件篩選才知道其價格(如飛機(jī)散客票);
某些產(chǎn)品配置很多資源,如上千酒店。
因此,得到一個產(chǎn)品的價格需要很多的數(shù)據(jù)和計(jì)算。另外,資源的價格會頻繁變化,比如機(jī)票的價格;或者因一批低價格的庫存賣光了那么這個資源就無法再以低價售賣,價格就要漲。這些情況就會使價格發(fā)生變化。這種變化的頻率很高,加上資源的數(shù)量較多,每天達(dá)到幾百萬次,且每個資源會被多個產(chǎn)品使用,所以每個資源變化都會引起多個產(chǎn)品的價格計(jì)算。這些因素造成價格中心面臨兩大難點(diǎn):計(jì)算復(fù)雜、計(jì)算量大。
二、重構(gòu)實(shí)踐
在整個系統(tǒng)優(yōu)化重構(gòu)的過程中我們遇到了很多問題,正是這些問題得到了很好的解決,才使得系統(tǒng)優(yōu)化成功。
●2.1 系統(tǒng)最關(guān)注什么?
系統(tǒng)最關(guān)注的是響應(yīng)時間?吞吐量?并發(fā)數(shù)?功能正確?還是穩(wěn)定性?這些都是我們關(guān)注的,但當(dāng)魚和熊掌不可兼得的時候,我們更關(guān)注什么?這個問題關(guān)系到我們的技術(shù)選型以及更深入地理解系統(tǒng)。
舉個例子,假設(shè)是一個對響應(yīng)時間要求很高的系統(tǒng),一個請求要求立即得到響應(yīng),那么就不太適合過多的異步處理方式。價格中心系統(tǒng)更專注吞吐量,即資源的價格變化必須能夠反映到產(chǎn)品的價格上。在響應(yīng)時間方面不是強(qiáng)制要求必須毫秒級或秒級,事實(shí)上只要幾秒內(nèi)可以變過來就可以,因?yàn)榭腿嗽趦r格變化的一瞬間預(yù)定產(chǎn)品的概率是很低的。某些場景下響應(yīng)時間可以更長,比如夜里刷價格,所以我們完全可以采用異步處理的方式。
●2.2 系統(tǒng)的瓶頸在哪里?
系統(tǒng)的性能優(yōu)化往往落到CPU/IO/內(nèi)存這三個里面(圖2)。那我們的系統(tǒng)瓶頸在哪里?
圖2 CPU/IO/內(nèi)存
這個問題決定著系統(tǒng)優(yōu)化該往哪走。拿價格中心系統(tǒng)舉例,假如CPU很低、IO時間很長、內(nèi)存使用量很大,就能得出一個結(jié)論:速度上不去,時間都消耗在IO上了,CPU得不到有效利用。進(jìn)而得出對策:提升并發(fā)線程數(shù),把CPU打上去。但是這樣做會帶來內(nèi)存的消耗增加,所以不合適。
經(jīng)過分析就會有疑問:這么多IO讀取出來的數(shù)據(jù)占用了內(nèi)存,而CPU較低,是不是有冗余的數(shù)據(jù)讀???帶著這個疑問再次檢查發(fā)現(xiàn)確實(shí)存在,這只是一個附帶的發(fā)現(xiàn),經(jīng)過分析我們基本確定的方向是降IO,通過控制計(jì)算規(guī)模降低內(nèi)存使用量。
●2.3 抽象領(lǐng)域模型
圖3 領(lǐng)域模型
抽象領(lǐng)域模型為了解決什么問題?
原系統(tǒng)中的功能邏輯混亂,耦合嚴(yán)重;
大家對一個功能如何實(shí)現(xiàn)缺乏統(tǒng)一的認(rèn)識;
產(chǎn)品和開發(fā)之間的溝通缺乏統(tǒng)一的語言。
領(lǐng)域模型凝聚了領(lǐng)域知識的元素,是系統(tǒng)運(yùn)行不可或缺的成員,但領(lǐng)域模型不具備系統(tǒng)整體運(yùn)行所具有的知識,只負(fù)責(zé)模型內(nèi)部的邏輯。
系統(tǒng)至少要有領(lǐng)域?qū)雍蛻?yīng)用層,領(lǐng)域模型在領(lǐng)域?qū)油瓿筛髯缘倪壿媽?shí)現(xiàn),應(yīng)用層再將這些領(lǐng)域模型關(guān)聯(lián)起來。我們經(jīng)過抽象將系統(tǒng)提煉出大的領(lǐng)域模型為資源、產(chǎn)品、促銷、交通、基礎(chǔ)等(圖3)。
●2.4 微服務(wù)的劃分
劃分微服務(wù)是為了解決以下問題:
功能耦合;
數(shù)據(jù)耦合,即沒有劃分和保護(hù)的數(shù)據(jù)被多個功能模塊依賴,導(dǎo)致一處數(shù)據(jù)的改動將波及大面積的功能模塊;
升級的影響范圍,因某個功能需要擴(kuò)充實(shí)例將導(dǎo)致所有實(shí)例都重新部署,某一個小功能引起的上線也將是整個應(yīng)用的上線。
具體做法是:通過抽象,基于之前建立的領(lǐng)域模型劃分出符合系統(tǒng)特點(diǎn)的微服務(wù),每個服務(wù)應(yīng)對系統(tǒng)的一類復(fù)雜度。不同的微服務(wù)之間在邏輯功能、被調(diào)用頻率、依賴的數(shù)據(jù)、實(shí)例個數(shù)等方面都是不一樣的。我們的系統(tǒng)經(jīng)過提煉劃分為資源管理服務(wù)、產(chǎn)品價格管理服務(wù)、計(jì)算控制服務(wù)、促銷計(jì)算服務(wù)、價格查詢服務(wù)。
微服務(wù)之間的配合推薦采用異步消息隊(duì)列的方式,這樣服務(wù)只關(guān)心自己的處理邏輯,而不用關(guān)心自己產(chǎn)生的數(shù)據(jù)被誰以哪種方式處理,服務(wù)之間是互相不感知的。
當(dāng)然這是這個系統(tǒng)的特點(diǎn)決定的,因?yàn)榭梢陨晕奚稽c(diǎn)響應(yīng)時間。通過RESTFUL接口的方式調(diào)用也是不錯的選擇,這時服務(wù)提供方一定是提供通用的服務(wù),即不會在自身邏輯里出現(xiàn)“當(dāng)調(diào)用者是XX的時候如何處理”這種判斷。
●2.5 控制每次計(jì)算的粒度
我們的系統(tǒng)還面臨一個特殊的問題:一個旅游產(chǎn)品由多種資源構(gòu)成,比如酒店/門票/機(jī)票等,正如前文所說,一個產(chǎn)品的價格計(jì)算可能有多種資源和選擇。
比如一個三亞5日游的產(chǎn)品,可以從北京/南京/上海/西安等多個城市出發(fā)(如圖4),這些城市的航班都不一樣,會有許多航班線路可供選擇,數(shù)量與可以去三亞的城市數(shù)量成正比。而這種城市數(shù)量會有多少,程序事先無法知道,完全由業(yè)務(wù)人員的產(chǎn)品設(shè)計(jì)決定。
這種情況帶來的問題就是:計(jì)算一個產(chǎn)品的價格時,不容易預(yù)先知道計(jì)算量大小,有時城市很少那么計(jì)算量就小,而有時出發(fā)去目的地的城市很多那么計(jì)算量就很大。
圖4 多地出發(fā)
這至少會帶來兩個麻煩:
一是系統(tǒng)的能力不容易評估,因?yàn)槊看渭夹g(shù)的粒度都不得而知,沒有統(tǒng)一的衡量標(biāo)準(zhǔn);
二是內(nèi)存會有很大的浪費(fèi)。
經(jīng)過分析,我們認(rèn)為從北京出發(fā)到三亞的產(chǎn)品價格和從上海去三亞的產(chǎn)品價格不要求在同一個時間產(chǎn)生,所以可以分開計(jì)算。這樣就可以縮小每次計(jì)算的粒度,使每次計(jì)算的粒度控制在一個相對原子的大小上。這就大大降低了內(nèi)存的使用,同時也加速了計(jì)算速度。
●2.6 無縫升級
每次升級都要考慮如何做到對外無感知、同時可回滾的設(shè)計(jì),將風(fēng)險控制在最低。一般如果涉及到數(shù)據(jù)庫結(jié)構(gòu)變化,可以選擇雙寫,新結(jié)構(gòu)數(shù)據(jù)庫穩(wěn)定之后再將老庫作廢。
但我們遇到一個問題:希望改變輸入規(guī)則,如圖5,將配置方式由藍(lán)色方式改為綠色方式,因?yàn)榫G色的方式更靈活,更符合業(yè)務(wù)需求,但問題是線上已經(jīng)有大量的藍(lán)色方式數(shù)據(jù)配置,把這些數(shù)據(jù)全刪掉再按照綠色方式配置上去是不可能的。
那么怎么做到平滑切換?除非找到一個算法將藍(lán)色配置映射成為綠色,遺憾的是沒有這種方法。
圖5 改變輸入規(guī)則
我們的做法是將藍(lán)色數(shù)據(jù)配置和綠色數(shù)據(jù)配置都向一個固定的模型去映射,把它們?nèi)恳暈橐?guī)則,那么藍(lán)色和綠色就是四種規(guī)則,如圖6所示。這樣就做到了新老數(shù)據(jù)共存和平滑升級。
圖6 規(guī)則
三、 技術(shù)實(shí)現(xiàn)
●3.1 擴(kuò)展立方體
隨著業(yè)務(wù)對計(jì)算資源、存儲資源的需求不斷增加,另一方面摩爾定律失效,人們無法找到擁有巨大資源又價格合適的計(jì)算機(jī)來支撐自己的業(yè)務(wù),所以轉(zhuǎn)而通過大量廉價的小型計(jì)算機(jī)一起工作來達(dá)到超級計(jì)算機(jī)的目的。當(dāng)計(jì)算需求增加,只要增加小型機(jī)的數(shù)量,就可以近似線性地增加系統(tǒng)性能。擴(kuò)展性,是分布式系統(tǒng)的重要考量因素。
圖7 擴(kuò)展立方體
在圖7所示的擴(kuò)展立方體中:
X軸代表每個結(jié)點(diǎn)同質(zhì)(同類型、同功能),只要增加結(jié)點(diǎn)數(shù)就可以增加系統(tǒng)處理能力;
Y軸代表基于不同業(yè)務(wù)的擴(kuò)展;
Z軸代表不同用戶類型(優(yōu)先級、地域等)的擴(kuò)展。
目前我們的系統(tǒng)主要是基于X軸和Y軸的擴(kuò)展。
我們的存儲水平擴(kuò)展方式是分表分庫,但分庫容易帶來跨庫Join的問題。我們是基于產(chǎn)品ID作為分表關(guān)鍵字的,基本上沒有產(chǎn)品之間需要關(guān)聯(lián)計(jì)算價格的場景,所以基本規(guī)避了跨庫Join的問題。
●3.2 RESTFUL調(diào)用組件
在服務(wù)治理上我們開發(fā)了一個RESTFUL的調(diào)用組件(圖8),通過它來解決服務(wù)發(fā)現(xiàn)/調(diào)用管理的問題。服務(wù)提供者將自身標(biāo)識注冊到注冊中心,服務(wù)消費(fèi)者通過注冊中心獲得可提供服務(wù)的列表。
圖8 RESTFUL調(diào)用組件
●3.3 消息隊(duì)列
我們在較多場景下使用了消息隊(duì)列。消息隊(duì)列的一個好處是解耦,在發(fā)送端看只需要將消息送到隊(duì)列,send and forget,不需要關(guān)注消息會被誰以哪種方式處理。
另外,負(fù)載均衡可以在消費(fèi)側(cè)完成,發(fā)送方無感知。這樣就可以動態(tài)地增加或減少消費(fèi)者數(shù)量。當(dāng)然這樣做也基本上要求業(yè)務(wù)的設(shè)計(jì)是無狀態(tài)、異步化的。
四、案例總結(jié)
●4.1 架構(gòu)是為業(yè)務(wù)服務(wù)的。
評價架構(gòu)的優(yōu)劣要看是否更好地支撐業(yè)務(wù)發(fā)展,而不是使用了什么技術(shù)。只有最適合本業(yè)務(wù)特點(diǎn)的架構(gòu)和技術(shù)選型才是好的。所以只有深刻理解業(yè)務(wù)本身才有好的架構(gòu)。
諸多開源中間件的出現(xiàn),減少了業(yè)務(wù)人員需要自己實(shí)現(xiàn)的功能(除非開源的項(xiàng)目不滿足要求),而架構(gòu)師也從設(shè)計(jì)實(shí)現(xiàn)更多地轉(zhuǎn)向?qū)I(yè)務(wù)需求的判斷,從而進(jìn)行架構(gòu)決策和技術(shù)選型與模式的運(yùn)用。
本案例是百億次計(jì)算量的系統(tǒng)優(yōu)化,其實(shí)第一個應(yīng)該討論的話題就是這100億次是否是必要的。事實(shí)上我們確實(shí)通過優(yōu)化降低了計(jì)算量,也降低了單次計(jì)算規(guī)模,這些都是建立在對業(yè)務(wù)的理解之上的。所以架構(gòu)永遠(yuǎn)是從業(yè)務(wù)出發(fā)。
●4.2 服務(wù)之間的邊界要清晰,盡量耦合小。
DDD的思想可以幫助我們找到服務(wù)邊界。
●4.3 盡量異步化設(shè)計(jì),這樣的耦合很小,邏輯上更清晰。
異步的系統(tǒng)要穩(wěn)定得多,某一個系統(tǒng)出現(xiàn)瓶頸的時候還有消息中間件幫助緩沖。同步的調(diào)用在等待時(線程會阻塞)會間接影響并發(fā)和吞吐量。另外,異步的系統(tǒng)在升級時會更容易,系統(tǒng)之間的限制要小一些,可以在隊(duì)列里面積壓一些。
●4.4 無狀態(tài)省去了很多負(fù)載均衡的煩惱, 不需要做黏性。
可以很容易地做到水平擴(kuò)展.有狀態(tài)系統(tǒng)在水平擴(kuò)展時是非常痛苦的。
●4.5 小步迭代,可回滾低風(fēng)險。
我們做到了每一次上線都是可回滾的,數(shù)據(jù)庫的設(shè)計(jì)在上線后的一段時間內(nèi)都是向下兼容的。而且新老數(shù)據(jù)是可以并存的。
11月9-12日,北京國家會議中心,第六屆TOP100全球軟件案例研究峰會,劉曉濤:途牛研發(fā)總監(jiān)將分享《天下武功唯快不破-微服務(wù)實(shí)踐快速響應(yīng)瞬息萬變的市場》。
TOP100全球軟件案例研究峰會已舉辦六屆,甄選全球軟件研發(fā)優(yōu)秀案例,每年參會者達(dá)2000人次。包含產(chǎn)品、團(tuán)隊(duì)、架構(gòu)、運(yùn)維、大數(shù)據(jù)、人工智能等多個技術(shù)專場,現(xiàn)場學(xué)習(xí)谷歌、微軟、騰訊、阿里、百度等一線互聯(lián)網(wǎng)企業(yè)的最新研發(fā)實(shí)踐。
TOP100大會將于11月9日-12日在北京國家會議中心舉辦,在現(xiàn)場通過對案例的復(fù)盤總結(jié),分享成功者背后的經(jīng)驗(yàn)和方法。大會開幕式單天體驗(yàn)票免費(fèi)入口。