宜人貸系統(tǒng)架構(gòu)——高并發(fā)下的進(jìn)化之路

演講嘉賓:宜人貸架構(gòu)師孫軍,擁有10 年的 Java 開發(fā)經(jīng)驗(yàn),先后在人民銀行、1 號(hào)店、人人網(wǎng)、當(dāng)當(dāng)網(wǎng)從事軟件開發(fā)與技術(shù)架構(gòu)工作。本次分享以宜人貸的系統(tǒng)迭代發(fā)展過程為主,著重介紹系統(tǒng)發(fā)展過程中遇到的實(shí)際問題和解決的辦法,并重點(diǎn)介紹宜人貸理財(cái)系統(tǒng)的高并發(fā)解決方案。

以下為數(shù)人云“高并發(fā)”活動(dòng)嘉賓演講實(shí)錄

宜人貸系統(tǒng)版本的迭代

1.0 版本——簡(jiǎn)單的煩惱
1.PNG

迭代之前宜人貸的系統(tǒng),其實(shí)就是一個(gè)前臺(tái),一個(gè)后臺(tái),一個(gè) DB ,前臺(tái)采用的是多機(jī)部署的方式。軟件層也是跟最傳統(tǒng)的軟件一樣分三層,第一層是 Controller ,第二層是 Service ,第三層是 DAO 。顯然這個(gè)系統(tǒng)并不適合互聯(lián)網(wǎng),有一些難以避免的問題。首先當(dāng)用戶過萬,在線用戶上千的時(shí)候,這樣的部署方式會(huì)產(chǎn)生一些瓶頸,包括服務(wù)器和數(shù)據(jù)庫(kù)兩方面。第二個(gè)就是團(tuán)隊(duì)規(guī)模變大,所有開發(fā)人員集中開發(fā)同一個(gè)系統(tǒng),沖突嚴(yán)重。

1.5 版本——“吃大補(bǔ)”試試!

針對(duì)上面的問題我們做了一些修改,我把它定義成“吃大補(bǔ)”。吃大補(bǔ)通常有一個(gè)很明顯的特點(diǎn),就是立馬見效,但是副作用也很大。

2.PNG

首先,我們?cè)谝巳速J的頁(yè)面層更加關(guān)注性能的提升,比如說使用瀏覽器緩存,壓縮傳輸,頁(yè)面都經(jīng)過了 YSlow 的優(yōu)化,鏈路層增加了 CDN ,做了靜態(tài)化甚至反向代理,這樣可以抵擋 80% 的流量。應(yīng)用服務(wù)器與數(shù)據(jù)庫(kù)層增加了一個(gè)緩存集群,這個(gè)緩存集群基本上又可以擋掉 80% 流量,最后系統(tǒng)層按照業(yè)務(wù)垂直拆分成多個(gè)系統(tǒng)。數(shù)據(jù)庫(kù)也有一些變化,開始只是一臺(tái)主機(jī),一臺(tái)數(shù)據(jù)庫(kù),現(xiàn)在變成了主從,甚至一主多從。用戶可以撐到過百萬,在線用戶上萬。即便如此,我們的制約因素依然在數(shù)據(jù)庫(kù),優(yōu)化的兩層雖然擋掉了大約 95% 的流量,但是業(yè)務(wù)發(fā)展依然超過了數(shù)據(jù)庫(kù)所能承受的負(fù)載,所以數(shù)據(jù)庫(kù)依然是一個(gè)很大的瓶頸。

第二個(gè)問題就是團(tuán)隊(duì)劃分,其實(shí)每個(gè)團(tuán)隊(duì)都做自己的系統(tǒng),但是大家仍然使用同一個(gè)數(shù)據(jù)庫(kù),這個(gè)時(shí)候?qū)τ谠O(shè)計(jì)和修改數(shù)據(jù)庫(kù),都非常麻煩。甚至每次都要問一下其他團(tuán)隊(duì),我這么改行不行,對(duì)你有什么影響等等。

第三個(gè)問題也非常棘手,大量使用了緩存,數(shù)據(jù)的時(shí)效性和一致性的問題越來越嚴(yán)重。

2.0 版本——“開小灶”精細(xì)化

為了解決 1.5 版本存在的問題,我們需要做精細(xì)化的優(yōu)化,我把它定義成開小灶。首先,合理規(guī)劃數(shù)據(jù)歸屬、優(yōu)化查詢效率、縮短數(shù)據(jù)庫(kù)事務(wù)時(shí)間;其次,分系統(tǒng),每個(gè)系統(tǒng)用固定的表。我們每天都要做的事,就是讓運(yùn)維找出線上最慢的SQL有哪些,對(duì)它們做優(yōu)化。第三,做去事務(wù),或者盡可能地縮短事務(wù)的時(shí)間。

然后開始關(guān)注代碼質(zhì)量,提高執(zhí)行效率,并且開始關(guān)注并發(fā)問題。用戶達(dá)到這個(gè)量的時(shí)候,就會(huì)有用戶幫我們測(cè)試并發(fā)問題。舉一個(gè)例子,同一個(gè)用戶用同一個(gè)帳戶登錄了兩個(gè)客戶端,他同時(shí)點(diǎn)取現(xiàn),這個(gè)時(shí)候如果程序處理的不好,很有可能讓他提現(xiàn)兩次。

最后,要區(qū)分強(qiáng)一致性與最終一致性的請(qǐng)求,合理使用緩存與讀寫分離來解決這些問題。

2.0 解決了很多性能問題,但還是會(huì)有新的問題——系統(tǒng)越來越多,系統(tǒng)間依賴關(guān)系變得復(fù)雜,這個(gè)時(shí)候很容易出現(xiàn) A 調(diào) B , B 調(diào) C , C 再調(diào)回 A 的循環(huán)調(diào)用。第二個(gè)是系統(tǒng)間互相調(diào)用增多,上游系統(tǒng)壓垮下游系統(tǒng);第三個(gè)也是非常頭疼的問題,系統(tǒng)很多,查找線上問題變得越來越困難——試想一下多個(gè)系統(tǒng)部署到很多機(jī)器上,想找一個(gè)線上的問題,通過日志的形式會(huì)非常難查。

所以在這個(gè)基礎(chǔ)上我們做了幾件事。一是限流。限流通常基于兩點(diǎn):最大活動(dòng)線程數(shù)和每秒運(yùn)行次數(shù);活動(dòng)最大線程數(shù)適合于高消耗的任務(wù),每秒運(yùn)行次數(shù)適合于低消耗的任務(wù)。第二,我建議在這個(gè)時(shí)期盡可能統(tǒng)一內(nèi)部系統(tǒng)間的返回值,返回值中一定要記錄返回狀態(tài)(業(yè)務(wù)正常、業(yè)務(wù)異常、程序異常)和錯(cuò)誤說明;第三,可重用RPC框架或在原框架基礎(chǔ)上繼續(xù)開發(fā)完成限流工作。

3.PNG

再說一下關(guān)于查找日志的問題。圖中為宜人貸日志系統(tǒng)部署框架,最左側(cè)的是我們的業(yè)務(wù)系統(tǒng),在業(yè)務(wù)系統(tǒng)上把日志收集到 Kafka 隊(duì)列,然后把 Kafka 隊(duì)列日志放到 ES 集群做索引,最終采用 Kibana 和我們自己研發(fā)的日志查詢系統(tǒng)去查看日志,這樣日志被集中到一個(gè)點(diǎn)后會(huì)更便于查找。

關(guān)于軟件方面,宜人貸統(tǒng)一使用 SLF4J+Logback 來輸出日志,然后業(yè)務(wù)系統(tǒng)間實(shí)現(xiàn)日志串聯(lián),所有服務(wù)端和客戶端之間都隱含地傳遞一些參數(shù),這些參數(shù)會(huì)隨著調(diào)用鏈一步一步往下傳,通過 AOP 來實(shí)現(xiàn)。日志串聯(lián)需要傳遞哪些參數(shù),或者日志中到底要打哪些參數(shù)呢?第一個(gè)是時(shí)間,這個(gè)時(shí)間應(yīng)該到毫秒級(jí),第二個(gè)是流水號(hào),流水號(hào)就是每次請(qǐng)求生成的一個(gè)唯一的值。然后是用戶 Session 、設(shè)備號(hào)、調(diào)用者時(shí)間( APP 使用手機(jī)本地時(shí)間)、本機(jī) IP 、客戶端 IP 、用戶真實(shí) IP 、跨越系統(tǒng)次數(shù)——如果我們發(fā)現(xiàn)了一個(gè)錯(cuò)誤,根據(jù)錯(cuò)誤日志可以找到流水號(hào),再通過流水號(hào)可以到日志查詢平臺(tái)查詢出這次請(qǐng)求途徑的所有系統(tǒng)和每個(gè)系統(tǒng)對(duì)這次請(qǐng)求的日志。有了這些找問題就非常容易。

做到 2.0 之后,宜人貸的網(wǎng)站基本能支撐中大型網(wǎng)站的規(guī)模,短時(shí)間內(nèi)不會(huì)有太多的性能問題了,但是我們依然會(huì)繼續(xù)往下走,進(jìn)一步提升系統(tǒng)版本。

3.0 版本——拆分做服務(wù)化

3.0 總結(jié)下來就是要做服務(wù)化,通俗一點(diǎn)說就是拆分,包括業(yè)務(wù)上的垂直拆分,以及垂直拆分基礎(chǔ)上的系統(tǒng)之上的水平拆分,那么服務(wù)化要怎么做呢?

首先,做業(yè)務(wù)拆分的時(shí)候,可以按照基礎(chǔ)服務(wù)和業(yè)務(wù)服務(wù)先做一個(gè)大的服務(wù)拆分,然后基礎(chǔ)服務(wù)又包括無業(yè)務(wù)型的基礎(chǔ)服務(wù)和有業(yè)務(wù)型的基礎(chǔ)服務(wù),無業(yè)務(wù)型的系統(tǒng)非常明顯跟其他的系統(tǒng)沒有太大的關(guān)系。而業(yè)務(wù)型基礎(chǔ)服務(wù)跟業(yè)務(wù)之間的關(guān)系很小,基本上跟業(yè)務(wù)系統(tǒng)之間的關(guān)聯(lián)關(guān)系僅限于主鍵和外鍵的關(guān)聯(lián)關(guān)系。

5.PNG

宜人貸可以天然地拆卸分成兩大系統(tǒng),一個(gè)是借款業(yè)務(wù),一個(gè)是理財(cái)業(yè)務(wù),借款業(yè)務(wù)可以拆分成后臺(tái)、 Web 、合作渠道等,這個(gè)系統(tǒng)之下會(huì)有一個(gè)基礎(chǔ)服務(wù),就是提供一些基礎(chǔ)服務(wù)和接口的一層系統(tǒng)。而基礎(chǔ)服務(wù)又拆成了兩部分,一個(gè)是基礎(chǔ)服務(wù)的進(jìn)件,一個(gè)是基礎(chǔ)服務(wù)的貸后。在拆分過程中我們又發(fā)現(xiàn)一個(gè)問題,理財(cái)和借款有兩個(gè)業(yè)務(wù)怎么拆都拆不開,就是撮合業(yè)務(wù)和債券關(guān)系,這種拆不開的可以單獨(dú)再提成一個(gè)系統(tǒng)來提供服務(wù)。

666.jpg

拆分系統(tǒng)看起來好像很容易,但是實(shí)際操作問題會(huì)很多。拆分的辦法我總結(jié)了如下幾個(gè):

第一,適當(dāng)冗余,冗余可以確保數(shù)據(jù)庫(kù)依然可以進(jìn)行關(guān)聯(lián)查詢。大部分重構(gòu)過程并不是做一個(gè)全新的系統(tǒng),而是在原來系統(tǒng)之上進(jìn)行修改,這個(gè)時(shí)候可以做一些冗余,避免修改代碼。

第二,數(shù)據(jù)復(fù)制,但必須保證數(shù)據(jù)歸屬系統(tǒng)有修改和發(fā)起復(fù)制的權(quán)限。這個(gè)比較適合于上文說的全局配置,比如說基本上所有公司都會(huì)有幾張表,記錄了全國(guó)的省市區(qū)縣,這些在每個(gè)系統(tǒng)中都會(huì)用,不一定每個(gè)系統(tǒng)都以接口的形式調(diào)用它,可以在每個(gè)系統(tǒng)里面都冗余一份數(shù)據(jù)。

第三,小技巧——如何驗(yàn)證數(shù)據(jù)庫(kù),并不一定非把它拆分成兩個(gè)物理的數(shù)據(jù)庫(kù)來驗(yàn)證,可以一個(gè)數(shù)據(jù)庫(kù)上建兩個(gè)帳號(hào),這兩個(gè)帳號(hào)分別的權(quán)限指向拆分之后的表,這樣就可以通過帳號(hào)來直接驗(yàn)證拆分效果。

第四,提前規(guī)劃服務(wù),拆分之前確定一下服務(wù)類型是讀多還是寫多的服務(wù),是快請(qǐng)求還是慢請(qǐng)求服務(wù),不同服務(wù)需要分開部署。

最后,同一數(shù)據(jù)不能由超過一個(gè)以上的系統(tǒng)控制,同一系統(tǒng)不能由超過一個(gè)以上的團(tuán)隊(duì)負(fù)責(zé)。

4.0 版本——云的展望

做到以上幾點(diǎn), 3.0 版本已經(jīng)做的差不多了,但是后面宜人貸依然還有很多要做的,4.0 版本是不是要做云平臺(tái),異地部署的方案,表很大的時(shí)候是不是要做垂直拆分,去 IOE 或者使用 Docker 快速部署等等這些,這些其實(shí)都是我們做 4.0 或者 5.0 將來要考慮的事情。

宜人貸理財(cái)系統(tǒng)的優(yōu)化

合理預(yù)估流量——強(qiáng)一致與最終一致

圖中這三個(gè)界面分別為首頁(yè)、列頁(yè),詳情頁(yè)。

6.5.PNG

在做優(yōu)化之前,首先要合理預(yù)估流量,常用方法有下面兩個(gè)。

評(píng)估方法一:平日 PV / 熱度時(shí)間;

評(píng)估方法二:熱度時(shí)間內(nèi)在線用戶數(shù) * 平均每人操作次數(shù)/熱度時(shí)間。以宜人貸理財(cái)端為例,假設(shè)在高峰時(shí)期有 N 萬人,然后平均做 M 次操作,在R分鐘左右基本上就把所有的債券搶光,計(jì)算出來大概是 N * M / R 萬次 / 秒。

預(yù)估完以后要做更細(xì)的預(yù)估,區(qū)分什么是強(qiáng)一致,什么是最終一致,這兩個(gè)流量分別是多少。強(qiáng)一致性要求請(qǐng)求的數(shù)據(jù)必須是當(dāng)時(shí)最準(zhǔn)確的數(shù)據(jù),這個(gè)數(shù)據(jù)不能用讀寫分離或緩存。最終一致性的數(shù)據(jù)時(shí)效性沒有那么高,只要最后的結(jié)果是正確的就可以。

假設(shè)這 M 次操作包含:注冊(cè)、注冊(cè)驗(yàn)證碼、登錄、解鎖手勢(shì)密碼、首頁(yè)、瀏覽產(chǎn)品列表等等這些操作,這里面其中有一些操作,比如說產(chǎn)品余額、生成訂單、支付短信、付款,這些都是強(qiáng)一致的要求。

針對(duì)最終一致的方案非常簡(jiǎn)單,增加機(jī)器就可以解決,實(shí)時(shí)性較高的可以直接使用數(shù)據(jù)庫(kù)的讀寫分離,如果使用 cache 的話,可以縮短 cache 時(shí)間;實(shí)時(shí)性較低的應(yīng)當(dāng)使用較長(zhǎng)時(shí)間的 cache 。

7.PNG

強(qiáng)一致性的流量處理方案,總的來說就是加鎖,可以使用數(shù)據(jù)庫(kù)的鎖,也可以使用 ZK ( Zookeeper )這樣的分布式鎖,或者直接使用隊(duì)列,因?yàn)殛?duì)列總得來說也是一種鎖。如果使用數(shù)據(jù)庫(kù)的鎖,基本上可以支持到并發(fā)在 2000 次每秒上下。使用數(shù)據(jù)庫(kù)的鎖來處理并發(fā),第一個(gè)方法就是有事務(wù)的處理并發(fā)。先開啟事務(wù),加鎖共享資源,然后再更新共享資源,最后再查詢一次共享資源,然后判斷一下結(jié)果。假如說這個(gè)結(jié)果是成立的,就直接繼續(xù)執(zhí)行,假如說這個(gè)結(jié)果是不成立的,直接回滾事務(wù)。第二個(gè)方法就是無事務(wù)的處理并發(fā),在數(shù)據(jù)庫(kù) SQL 的 where 條件加上判斷條件,如果 update 條數(shù)為 1 則更新成功,如果為 0 則更新失敗,這時(shí)需要用寫代碼的形式回滾數(shù)據(jù)。

如果流量依然承受不住該怎么辦?

做到這些其實(shí)已經(jīng)能夠承受非常大的流量,但是業(yè)務(wù)可能繼續(xù)發(fā)展,還承受不住怎么辦呢?

首先的一個(gè)原則就是,沒有任何一個(gè)分布式算法適合并發(fā)操作,最好的方法就是單點(diǎn)并排隊(duì)進(jìn)行處理。

第二,單點(diǎn)并發(fā)過大,使用合適的方式拆分鎖的粒度。

第三,增加降級(jí)需求,不影響用戶正常使用情況下可以適當(dāng)降低服務(wù)質(zhì)量。適當(dāng)修改需求、適當(dāng)增加用戶等待結(jié)果的時(shí)間;如果讓用戶多等一倍的時(shí)間,可能就能承受之前兩倍的并發(fā),這個(gè)可以在交互上優(yōu)化,讓用戶有更好的體驗(yàn)。

最后,適當(dāng)調(diào)整運(yùn)營(yíng)策略,分散用戶的集中活躍時(shí)間。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容