分布式架構(gòu)洞察與拆解
一、詳解高并發(fā)系統(tǒng)的關(guān)鍵指標(biāo)
在設(shè)計(jì)一個(gè)軟件系統(tǒng)的時(shí)候,我們需要對(duì)系統(tǒng)性能進(jìn)行整體的評(píng)估,下面就是需要我們?cè)谌粘_M(jìn)行系統(tǒng)評(píng)估和調(diào)優(yōu)分析的關(guān)鍵指標(biāo):
(1) RT(response time):響應(yīng)時(shí)間,它代表發(fā)送請(qǐng)求到響應(yīng)數(shù)據(jù)所花費(fèi)的時(shí)間,也可以理解我們web開發(fā)領(lǐng)域所謂的接口響應(yīng)耗時(shí),它反映了系統(tǒng)響應(yīng)的快慢。
(2) throughput:也就是我們常說的吞吐量,它在不同的領(lǐng)域都有著不同的含義:
- 業(yè)務(wù)角度:吞吐量可以代表請(qǐng)求數(shù)/秒、人/天或者 處理的業(yè)務(wù)數(shù)/小時(shí)
- 網(wǎng)絡(luò)角度:從網(wǎng)絡(luò)角度來看,可直接用字節(jié)數(shù)/秒
針對(duì)廣義上互聯(lián)網(wǎng)領(lǐng)域,該指標(biāo)更多反應(yīng)的是系統(tǒng)的負(fù)載能力,在沒有性能瓶頸時(shí),服務(wù)器吞吐量計(jì)算公式為:
F= VU(虛擬用戶數(shù)) * R(虛擬用戶發(fā)出請(qǐng)求數(shù)) /T (測(cè)試時(shí)花費(fèi)的時(shí)間)
- QPS(每秒請(qǐng)求數(shù)):指服務(wù)器一秒內(nèi)處理了多少個(gè)請(qǐng)求,主要用于表示讀請(qǐng)求。
- TPS(每秒事務(wù)數(shù)): 以高并發(fā)系統(tǒng)中TPS中所指的事務(wù)數(shù)包括3個(gè)過程:客戶端請(qǐng)求服務(wù)端、服務(wù)端內(nèi)部執(zhí)行業(yè)務(wù)邏輯、服務(wù)端響應(yīng)結(jié)果給客戶端,
這里很多讀者可能對(duì)于TPS和QPS的概念有寫混淆,實(shí)際上TPS相較于QPS來說更能反應(yīng)服務(wù)點(diǎn)單位時(shí)間內(nèi)的一個(gè)完整的原子的事務(wù)的處理能力。 例如我們現(xiàn)在有個(gè)web頁(yè)面,當(dāng)用戶打開到完全加載查看到頁(yè)面內(nèi)容時(shí),這就可以理解為1個(gè)TPS,而這個(gè)頁(yè)面中所有請(qǐng)求的接口就應(yīng)該計(jì)入QPS中。
- PV(訪問量): 也就是page view,頁(yè)面瀏覽量的意思,用戶每對(duì)網(wǎng)站中一個(gè)完整的網(wǎng)頁(yè)進(jìn)行一次瀏覽就可以視為PV+1。
- UV(獨(dú)立訪客):unique visitor指代某個(gè)網(wǎng)站或者或者鏈接中出現(xiàn)的不同的IP的數(shù)量,我們可以把ip地址視為這個(gè)訪客的身份證,這意味著某個(gè)ip某天無(wú)論訪問網(wǎng)站多少次均視為一次,這也意味著UV代表著網(wǎng)站某天有多少獨(dú)立訪客進(jìn)行訪問。
二、數(shù)據(jù)一體化模式
1. 詳解數(shù)據(jù)一體化模式
軟件發(fā)展初期,大部分軟件應(yīng)用都以門戶、OA等系統(tǒng)為主,這些系統(tǒng)無(wú)論在訪問人數(shù)還是系統(tǒng)數(shù)據(jù)量都是有限的。所以單體服務(wù)器就可以輕松解決這些問題。所以此時(shí)的軟件架構(gòu)著重追求的簡(jiǎn)單和快速迭代、成本低,所以所有程序和數(shù)據(jù)庫(kù)等部署在一臺(tái)服務(wù)器上,因?yàn)檫@種架構(gòu)是將應(yīng)用和數(shù)據(jù)庫(kù)都存放在一臺(tái)服務(wù)器上,所以我們一般稱這種部署模式為數(shù)據(jù)一體化模式:
2. 監(jiān)控時(shí)如何判定當(dāng)前服務(wù)器是否正常
針對(duì)業(yè)務(wù)初期的數(shù)據(jù)一體化模式,性能瓶頸大概率要依賴與系統(tǒng)服務(wù)器指標(biāo)監(jiān)測(cè),這里我們針對(duì)一個(gè)8C16G服務(wù)器并獨(dú)立部署和一個(gè)單體應(yīng)用和數(shù)據(jù)庫(kù)的場(chǎng)景,給出一個(gè)比較合理的指標(biāo)參考:
- CPU利用率:一般建議高負(fù)載情況下不要超70%。
- 負(fù)載:?jiǎn)魏素?fù)載超過0.7就說明有問題,所以負(fù)載一般要求卡在5.6(8核*0.7)以內(nèi)算正常。
- 磁盤利用率:一般來說超過70%時(shí)就需要考慮清理一些過期的無(wú)用的垃圾數(shù)據(jù)了。
- 堆內(nèi)存占用:一般建議堆內(nèi)存不要分配超過機(jī)器的一半,可以適當(dāng)多一些。
- 內(nèi)存使用率:結(jié)合上述堆內(nèi)存的配置,一般來說內(nèi)存使用率不要超過70%超過則說明可能有內(nèi)存泄漏或者程序吞吐量下降的風(fēng)險(xiǎn)。
- GC次數(shù)和時(shí)長(zhǎng):這個(gè)更多是經(jīng)驗(yàn)之談,理論來說full gc不要在也為高峰期出現(xiàn)就行了,而每次垃圾回收時(shí)常stw一般不要超過200ms,這樣對(duì)用戶來說是無(wú)感知的,而minor gc最壞的場(chǎng)景一般要求每分鐘不能超過1次,每次50ms以內(nèi)比較合理。
關(guān)于系統(tǒng)負(fù)載情況的評(píng)估可以參考這篇文章:Understanding Linux CPU Load - when should you be worried?:https://scoutapm.com/blog/understanding-load-averages
三、應(yīng)用與數(shù)據(jù)分離模式
1. 應(yīng)用與數(shù)據(jù)分離架構(gòu)簡(jiǎn)介
基于這個(gè)架構(gòu)我們的業(yè)務(wù)體量有所增加,此時(shí)無(wú)論是活躍用戶數(shù)量還是數(shù)據(jù)量都有所上升,這時(shí)候我們一般都會(huì)考慮增加資源,于是我們將應(yīng)用和數(shù)據(jù)庫(kù)分離。
這樣做的好處是讓軟件和數(shù)據(jù)庫(kù)可以專注于各自維度的優(yōu)化,即應(yīng)用是對(duì)外提供服務(wù)的,更著重追求CPU和內(nèi)存。而數(shù)據(jù)庫(kù)因?yàn)樾枰褦?shù)據(jù)進(jìn)行存儲(chǔ)和索引等IO操作,更著重的是IO性能以及磁盤轉(zhuǎn)速,當(dāng)然對(duì)于內(nèi)存要求也是有的,所以將數(shù)據(jù)庫(kù)從應(yīng)用服務(wù)器中分離將其部署在一臺(tái)磁盤空間更大、IO性能更好的服務(wù)器上,由此提升應(yīng)用整體吞吐量:
2. 關(guān)于單體應(yīng)用啟動(dòng)前系統(tǒng)指標(biāo)飆升問題
通過數(shù)據(jù)與應(yīng)用分離,我們得到了一個(gè)更加存粹的應(yīng)用系統(tǒng)服務(wù)器,此時(shí)調(diào)優(yōu)工作會(huì)更著重于應(yīng)用,假如應(yīng)用啟動(dòng)后前幾分鐘,Load、RT、CPU等飆高,如何定位,可能的原因是什么?
注意問題所強(qiáng)調(diào)的啟動(dòng)后的幾分鐘,說明這些問題大部分是發(fā)生在啟動(dòng)前,一般來說服務(wù)啟動(dòng)可能存在如下問題:
- 灰度發(fā)布
- 資源初始化預(yù)熱
針對(duì)問題1,這是分布式場(chǎng)景的問題,我們簡(jiǎn)單說明一下,假如我們?nèi)粘?00臺(tái)服務(wù)器剛好承載業(yè)務(wù)流量,灰度發(fā)布是批次升級(jí),這期間就可能存在不到100臺(tái)服務(wù)承載100臺(tái)服務(wù)器的流量,在灰度升級(jí)期間就可能存在上述指標(biāo)飆高的情況。
這對(duì)問題2,就是我們的單體架構(gòu)的場(chǎng)景了,我們的服務(wù)啟動(dòng)前都需要連接池、線程池、緩存預(yù)熱的預(yù)熱等工作,針對(duì)這種情況我們可以用例如arthas等工具進(jìn)行定位排查,確保這些工作完成后再將服務(wù)暴露出去。
除了上述情況,還有可能和java語(yǔ)言本身的JIT機(jī)制優(yōu)化,在服務(wù)初始化時(shí),大部分代碼執(zhí)行過程都是通過解釋后在運(yùn)行的,只有在運(yùn)行一段時(shí)間成為熱點(diǎn)代碼后才會(huì)生成機(jī)器碼緩存,由此提升程序運(yùn)行效率,針對(duì)該問題解決思路也有很多,例如:
- 提前在業(yè)務(wù)上運(yùn)行這段代碼進(jìn)行預(yù)熱。
- 使用JwarmUp技術(shù)保證上一次啟動(dòng)后的信息存儲(chǔ)到文件中,確保下次啟動(dòng)時(shí)通過讀取該文件提前完成JIT優(yōu)化。
四、緩存與性能的提升
業(yè)務(wù)體量還在增加,特定場(chǎng)景下某些數(shù)據(jù)是被用戶頻繁查詢的熱點(diǎn)數(shù)據(jù),在之前的架構(gòu)下,獲取這些信息的方式都是通過數(shù)據(jù)庫(kù)查詢獲取,因此數(shù)據(jù)庫(kù)在這個(gè)節(jié)點(diǎn)很可能稱為系統(tǒng)的瓶頸。所以我們就考慮引入一個(gè)緩存中間件,針對(duì)熱點(diǎn)數(shù)據(jù)或是通過預(yù)加載或是數(shù)據(jù)庫(kù)查詢后緩存的方式存放到內(nèi)存中,從而提升熱點(diǎn)數(shù)據(jù)的查詢性能。
需要了解的是,一般情況下,緩存中間件可以和應(yīng)用程序放在一起,但還是本著獨(dú)立自治且合理利用資源的原則,我們一般建議將緩存也專門部署到一臺(tái)服務(wù)器上,讓其享有較大的內(nèi)存空間以加載更多的數(shù)據(jù):
有了緩存中間件之后,我們除了可以在服務(wù)啟動(dòng)時(shí)提前加載熱點(diǎn)數(shù)據(jù),對(duì)于某些不可提前預(yù)知的數(shù)據(jù),我們可以通過如下步驟完成:
- 查看redis是否有熱點(diǎn)數(shù)據(jù),如果有則直接返回,如果沒有則進(jìn)入步驟2。
- 數(shù)據(jù)庫(kù)查詢,如果有則加載至緩存中,并將結(jié)果返回。
因?yàn)榫彺鏀?shù)據(jù)位于內(nèi)存,相較于數(shù)據(jù)庫(kù)那種需要通過磁盤IO(大部分情況下,其實(shí)數(shù)據(jù)也有buffer pool)響應(yīng)速度會(huì)更快一些。 但是隨之而來的就是可用性和一致性等問題,例如:
- 緩存中間掛了怎么辦?
- 緩存服務(wù)器宕機(jī)怎么辦?
- 如何解決數(shù)據(jù)庫(kù)和緩存中間件數(shù)據(jù)一致性問題。
這些筆者都會(huì)在后續(xù)系列文章中補(bǔ)充說明。
五、分布式負(fù)載均衡
1. 分布式負(fù)載均衡架構(gòu)模式
引入緩存中間件進(jìn)一步提升了系統(tǒng)吞吐量,這時(shí)候就要考慮高并發(fā)請(qǐng)求性能瓶頸了,隨著業(yè)務(wù)發(fā)展用戶的請(qǐng)求量也會(huì)不斷的提升,這也就是我們常說的高并發(fā)問題了,
此時(shí)我們的應(yīng)用只單臺(tái)服務(wù)器上,盡管各種通過各種參數(shù)調(diào)優(yōu)和程序優(yōu)化提升系統(tǒng)整體吞吐量,但畢竟應(yīng)用是依賴于系統(tǒng)服務(wù)器的,最終都會(huì)在一個(gè)閾值上進(jìn)入瓶頸而無(wú)法進(jìn)一步優(yōu)化,所以我們還是采用水平拓展的方式構(gòu)成服務(wù)器集群,即通過更多的服務(wù)器承載單位時(shí)間內(nèi)的并發(fā)請(qǐng)求,從而提升系統(tǒng)整體的吞吐量。
如下圖可以看到,本次架構(gòu)本質(zhì)上是通過橫向拓展的方式讓負(fù)載均衡器通過負(fù)載均衡算法將請(qǐng)求分散到不同的應(yīng)用服務(wù)器上,借著負(fù)載均衡器,我們還可以針對(duì)系統(tǒng)整體做流控、鑒權(quán)、反向代理等統(tǒng)一請(qǐng)求管理,保證系統(tǒng)整體安全和穩(wěn)定:
2. 系統(tǒng)的QPS與服務(wù)器數(shù)量評(píng)估
針對(duì)分布式負(fù)載均衡,進(jìn)行橫向拓展時(shí)我們必須結(jié)合業(yè)務(wù)場(chǎng)景獲得一個(gè)比較準(zhǔn)確的估值進(jìn)行服務(wù)器部署,假設(shè)每日有500w的請(qǐng)求,按照業(yè)界的二八原則即80%的流量都發(fā)生在20%的時(shí)間段,對(duì)此我們的推算系統(tǒng)QPS的步驟為:
一天24小時(shí),而流量基本都發(fā)生在白天,所以我們實(shí)際要關(guān)注的時(shí)間段應(yīng)該是12h。 對(duì)應(yīng)20%的時(shí)間段即指代12*0.2即某個(gè)時(shí)間段的2h承載80%的流量,所以我們可以得到如下的計(jì)算:
(流量*80%)/(高峰時(shí)間段小時(shí))/(每小時(shí)有多少秒)
(500_0000 * 0.8) / (12 * 0.2)/(60 * 60) ≈ 460
由此我們可以推算出系統(tǒng)日常情況下是QPS為460,為預(yù)防突發(fā)流量,所以峰值應(yīng)該是平均高峰值的2~3倍數(shù),所以我們認(rèn)為當(dāng)前場(chǎng)景下QPS最高應(yīng)該是1500左右。 假設(shè)單臺(tái)機(jī)器可承受的QPS為300,換算一下1500/300,我們大約需要5臺(tái)左右的服務(wù)器,然后基于這個(gè)基準(zhǔn)的值提前設(shè)置一下限流、熔斷策略,由此高性能和高可用。
3. 服務(wù)器選型
有了上述的服務(wù)器配算方案之后,我們就需要針對(duì)性選擇何時(shí)的服務(wù)器,假設(shè)我們有4C8G *16臺(tái) 和 8C16G*8臺(tái),我們?cè)撨x擇那種方案呢?
兩者服務(wù)器各有各的優(yōu)勢(shì),對(duì)此我們可以從以下幾個(gè)角度考慮問題:
- 單機(jī)瓶頸:這一點(diǎn)很明顯后者表現(xiàn)更加出色,假如遇到某些功能場(chǎng)景需要更多的核心數(shù)和內(nèi)存,前者則會(huì)力不從心。
- 容錯(cuò):前者機(jī)器更多,及時(shí)單臺(tái)機(jī)器出現(xiàn)故障,對(duì)于系統(tǒng)的整體影響面相較于后者會(huì)小些。
- 負(fù)載均衡:因?yàn)榍罢哂兄嗟臋C(jī)器,所以對(duì)于負(fù)載均衡這種橫向拓展問題發(fā)揮的空間會(huì)更大一些。
- 連接數(shù):MySQL分配給單機(jī)的連接數(shù)是固定的,且大公司企業(yè)都不允許調(diào)整連接數(shù),這也就意味著有著更多的機(jī)器總的連接數(shù)也就越多。
- GC效率:從垃圾回收器來說掃描垃圾的開銷遠(yuǎn)小于回收垃圾的開銷,單機(jī)有著更大的內(nèi)存空間減少垃圾回收次數(shù)自然是更好的做法。
總體來說,4C8G *16臺(tái)更符合現(xiàn)代微服務(wù)的理念,服務(wù)器更加輕量級(jí),通過堆疊更多的服務(wù)器分散壓力,對(duì)于并發(fā)處理能力、容錯(cuò)能力、負(fù)載均衡、連接數(shù)等方面更加友好,但還是需要考慮單機(jī)性能瓶頸和GC次數(shù)和擴(kuò)容效率帶來的影響。
4. 分布式架構(gòu)一定比單體架構(gòu)要好
單體架構(gòu)也就是我們上說數(shù)據(jù)一體化模式及其進(jìn)階版本,一般發(fā)生在企業(yè)創(chuàng)業(yè)初期,為了快速進(jìn)入市場(chǎng)時(shí)所誕生的系統(tǒng),它要求系統(tǒng)越簡(jiǎn)單越好,搭建過程也是越快越好,一般來說單體架構(gòu)標(biāo)配為:
- 一個(gè)應(yīng)用程序
- 一個(gè)數(shù)據(jù)
- 一個(gè)文件服務(wù)器
它的優(yōu)點(diǎn)如下:
- 開發(fā)、測(cè)試、部署成本都非常低,只需針對(duì)一個(gè)項(xiàng)目開展,基本上可以做到前腳修改吼叫直接打包部署上線。
- 單體程序網(wǎng)絡(luò)基本都是數(shù)據(jù)庫(kù)、redis等,不需要通過RPC與其他服務(wù)做交互,大大降低網(wǎng)絡(luò)延遲。
- 因?yàn)闃I(yè)務(wù)邏輯全部打包在單體上,不需考慮分布式鎖、分布式事務(wù)、分布式id問題,只需專注解決單機(jī)并發(fā)問題即可。
由此缺點(diǎn)也很明顯:
- 單體架構(gòu)在業(yè)務(wù)量增加后容易導(dǎo)致性能瓶頸。
- 單體架構(gòu)所有業(yè)務(wù)都耦合在一個(gè)程序上,隨著業(yè)務(wù)演進(jìn),多人協(xié)作開發(fā)時(shí)代碼耦合度非常高,測(cè)試回歸成本也會(huì)急劇增加,隨著時(shí)間推移可能導(dǎo)致開發(fā)節(jié)奏混亂、業(yè)務(wù)邊界不清晰、分支合并各種沖突導(dǎo)致各種潛在的風(fēng)險(xiǎn)。
- 單點(diǎn)故障會(huì)導(dǎo)致業(yè)務(wù)全癱。
- 這也是最為嚴(yán)重的一點(diǎn),單體架構(gòu)隨著業(yè)務(wù)的拓展,按照用戶展示層、業(yè)務(wù)邏輯層、數(shù)據(jù)訪問層這種模式進(jìn)行分層邏輯,在業(yè)務(wù)不斷迭代之后,系統(tǒng)調(diào)用會(huì)構(gòu)成下圖所示的網(wǎng)狀結(jié)構(gòu),在水平方向上來看確實(shí)比較精簡(jiǎn),但是從垂直的角度來看,各層之間相互錯(cuò)中復(fù)雜的調(diào)用,導(dǎo)致各層都會(huì)存在依賴一個(gè)或者多個(gè)模塊的情況,這對(duì)于后續(xù)的功能復(fù)用、維護(hù)、甚至是不同模塊間的技術(shù)棧升級(jí)都是異常困難的。
而分布式架構(gòu)對(duì)應(yīng)的優(yōu)點(diǎn)有:
- 不同服務(wù)分布在不同的服務(wù)器上,橫向拓展方便,將一個(gè)復(fù)雜的問題拆解到不同的服務(wù)上,業(yè)務(wù)邊界更加清晰,降低團(tuán)隊(duì)的協(xié)作成本。
- 可以靈活結(jié)合業(yè)務(wù)場(chǎng)景進(jìn)行服務(wù)拆分、堆加服務(wù)器,快速拓展以抗住高并發(fā)流量。
- 模塊化開發(fā),易于功能拓展和維護(hù)。
- 單點(diǎn)故障問題相較于單體架構(gòu)來說會(huì)相對(duì)少一些。
對(duì)應(yīng)缺點(diǎn):
- 運(yùn)維成本高,分布式場(chǎng)景我們需要針對(duì)多個(gè)服務(wù)進(jìn)行部署、監(jiān)控、日志、故障恢復(fù),增加系統(tǒng)的復(fù)雜度,且對(duì)于分布式場(chǎng)景風(fēng)險(xiǎn)眾多,需要考慮的問題場(chǎng)景相較于前者更多,對(duì)于運(yùn)維、開發(fā)要求會(huì)更高。
- 故障定位,分布式架構(gòu)相較于前者更復(fù)雜,一個(gè)請(qǐng)求需要經(jīng)過很多個(gè)服務(wù),出現(xiàn)問題時(shí)的排查成本相較于單體架構(gòu)來說會(huì)更高,由此還得借助分布式鏈路追蹤。
- 服務(wù)邊界拆分維度也很困難,拆分粒度小了對(duì)于多服務(wù)運(yùn)維和監(jiān)控成本會(huì)增加,拆分粒度過粗又會(huì)導(dǎo)致服務(wù)后期變?yōu)橐粋€(gè)臃腫的單體。
- 分布式一致性問題,臨界資源考慮的維度都是面向分布式場(chǎng)景,這其中就會(huì)面臨分布式事務(wù)和分布式鎖一致性問題。
所以對(duì)于開發(fā)周期短、業(yè)務(wù)量小、追求快速上線優(yōu)先考慮使用單體架構(gòu),只有業(yè)務(wù)增長(zhǎng),需要考慮高并發(fā)和高可用的場(chǎng)景才需要考慮分布式和微服務(wù)這些。
六、讀寫分離
我們通過緩存中間件解決了熱點(diǎn)數(shù)據(jù)問題,但是緩存中間件的容量畢竟是有限的,對(duì)于非熱點(diǎn)數(shù)據(jù)我們還是需要通過數(shù)據(jù)庫(kù)進(jìn)行統(tǒng)一維護(hù)管理為了避免數(shù)據(jù)庫(kù)寫操作(update、insert、delete)阻塞其他事務(wù)讀操作(select),因?yàn)楝F(xiàn)代主流系統(tǒng)架構(gòu)基本都是讀多寫少的,所以我們更傾向于引入讀寫分離的方案,即讓所有寫操作都都面向master服務(wù)器,而讀操作都通過slave服務(wù)器完成,slave通過訂閱master庫(kù)的bin.log進(jìn)行數(shù)據(jù)實(shí)時(shí)同步操作。
在此基礎(chǔ)上我們針對(duì)表進(jìn)行優(yōu)化設(shè)計(jì),減小應(yīng)用請(qǐng)求進(jìn)行寫操作時(shí)上X鎖的粒度,保證寫操作的吞吐量,與此同時(shí)因?yàn)樽x操作上的都是S鎖,而S鎖之間都是彼此兼容,由此通過讀寫分離加S鎖兼容性,更進(jìn)一步提升系統(tǒng)整體吞吐量。
七、引入CDN緩存
實(shí)際上,我們進(jìn)行web網(wǎng)站瀏覽時(shí),大部分都是基本不變的靜態(tài)數(shù)據(jù)例如:JS腳本、HTML、圖片文件等,考慮到訪問的用戶位于不同的城市,在針對(duì)這些系統(tǒng)資源加載的時(shí)可能會(huì)經(jīng)過好幾個(gè)網(wǎng)絡(luò)路由轉(zhuǎn)發(fā)才能完成資源下載,所以我們提出CDN技術(shù)即資源分發(fā)網(wǎng)絡(luò)(Content Delivery Network),而CDN的大體工作流程是:
- 用戶通過域名針對(duì)網(wǎng)站發(fā)起資源請(qǐng)求,首先向LDNS(本地DNS)進(jìn)行域名請(qǐng)求。
- DNS解析域名得到對(duì)應(yīng)的CNAME對(duì)應(yīng)的IP地址,從而定位到對(duì)應(yīng)的服務(wù)商的服務(wù)器。
- 重點(diǎn)來了,服務(wù)提供商通過DNS調(diào)取系統(tǒng)返回給用戶最佳IP地址。
- 查看該服務(wù)器是否有請(qǐng)求的靜態(tài)資源,如果沒有則緩存并返回,若有則直接返回。
可以看到,通過CDN減輕了應(yīng)用服務(wù)器的壓力,大大提升的web頁(yè)面的響應(yīng)速度,保證了可用性和高性能:
八、分庫(kù)分表
1. 詳解分庫(kù)分表理論思路
經(jīng)過上述幾輪的架構(gòu)演進(jìn),軟件的系統(tǒng)架構(gòu)已經(jīng)趨于穩(wěn)定,隨著時(shí)間的推移數(shù)據(jù)庫(kù)中的數(shù)據(jù)體量也會(huì)隨之逐步增加,即使通過讀寫分離單表數(shù)據(jù)體量已無(wú)法保證大表的快速檢索,同時(shí)也考慮到并發(fā)請(qǐng)求的體量也在逐步增加每個(gè)數(shù)據(jù)庫(kù)所能承受的最大連接數(shù)也已經(jīng)無(wú)法滿足現(xiàn)有的業(yè)務(wù)需求,所以我們又不得不考慮更進(jìn)一輪的水平拓展。即分庫(kù)分表方案將大表拆小,增加幾臺(tái)數(shù)據(jù)庫(kù)服務(wù)器來分?jǐn)傔@些數(shù)據(jù)管理。
因?yàn)閿?shù)據(jù)庫(kù)分散部署,所以在進(jìn)行分庫(kù)分表設(shè)計(jì)時(shí)我們還需要結(jié)合一定的算法保證數(shù)據(jù)維護(hù)和檢索的開銷,為此我們可能還需要引入分庫(kù)分表中間件來管理應(yīng)用程序的數(shù)據(jù)庫(kù)訪問工作:
2. 如果單表數(shù)據(jù)量大,只能考慮分庫(kù)分表嗎
分庫(kù)分表是綜合解決并發(fā)和數(shù)據(jù)檢索效率的綜合方案,但是分庫(kù)分表后會(huì)帶來問題:
- 跨庫(kù)表分頁(yè)
- 分布式事務(wù)
- 非分片鍵值的數(shù)據(jù)查詢
- 跨庫(kù)表join關(guān)聯(lián)查詢
針對(duì)單表數(shù)據(jù)量大,我們可以從以下幾個(gè)維度進(jìn)行排查和按需設(shè)計(jì):
- 并發(fā)量不是很大的場(chǎng)景下,針對(duì)單表做好設(shè)計(jì),建立何時(shí)的索引并采取相對(duì)簡(jiǎn)單的輕量級(jí)分區(qū)設(shè)計(jì)保證查詢檢索效率即可。
- 單表數(shù)據(jù)量大,但是特定業(yè)務(wù)下這張表每日只有一些相對(duì)局限的熱點(diǎn)數(shù)據(jù)查詢請(qǐng)求,針對(duì)這些請(qǐng)求條件建立好表索引,然后借助緩存中間件來存儲(chǔ)這些熱點(diǎn)數(shù)據(jù)。
- 如果需要保證高并發(fā)和高吞吐量,我們則需要借助分庫(kù)分表來分散并發(fā)量和單表體量提升檢索性能。
九、分布式架構(gòu)
1. 詳解分布式結(jié)構(gòu)模式
此時(shí)我們就已經(jīng)具備了一個(gè)性能性能較為出色的分布式系統(tǒng)架構(gòu),但是我們所有的業(yè)務(wù)都耦合在一個(gè)應(yīng)用程序上,無(wú)法針對(duì)單個(gè)原子業(yè)務(wù)進(jìn)行管理和優(yōu)化,甚至說在高并發(fā)場(chǎng)景下,一些訪問量和體量都比較小的業(yè)務(wù)會(huì)因?yàn)橐恍狳c(diǎn)業(yè)務(wù)導(dǎo)致整體癱瘓,無(wú)法對(duì)外提供服務(wù)。
于是我們會(huì)嘗試通過圈表并結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景針對(duì)性的進(jìn)行業(yè)務(wù)拆分,將不同的業(yè)務(wù)部署到不同的服務(wù)器上,因?yàn)檫@些應(yīng)用之間還是存在的關(guān)聯(lián),相互之間還會(huì)存在著調(diào)用關(guān)系,所以我們還會(huì)考慮引入注冊(cè)中心、配置中心、消息隊(duì)列等中間件協(xié)調(diào)應(yīng)用之間的通信:
2. 分布式業(yè)務(wù)架構(gòu)流量突增問題
分布式系統(tǒng)架構(gòu)下勢(shì)必存在的訪問量暴增的特點(diǎn),假設(shè)QPS突增100倍你會(huì)怎么處理?一般來說在上文中我們已經(jīng)針對(duì)系統(tǒng)可承受的范圍內(nèi)做了評(píng)估并預(yù)設(shè)了指定數(shù)量服務(wù)器來承載這些流量,對(duì)于突增的流量大部分情況下都可以拒絕掉,以保證部分用戶可用以及保證系統(tǒng)能夠維穩(wěn)運(yùn)行。
對(duì)此該問題,一般出現(xiàn)業(yè)務(wù)流量突增我們必須考慮以下兩種情況:
- 異常情況:服務(wù)被ddos攻擊了。
- 正常情況:引流或者某些原因?qū)е聵I(yè)務(wù)量暴增。
針對(duì)第一種情況,我們必須采取相應(yīng)防護(hù)手段處理,例如:
- 識(shí)別攻擊源。在防火墻服務(wù)器調(diào)整ACL(訪問控制列表)阻斷這些攻擊源。
- nginx進(jìn)行限流
- 適當(dāng)增加服務(wù)器通過負(fù)載均衡分擔(dān)流量。
- 針對(duì)帶寬消耗型的攻擊,增加帶寬適當(dāng)緩解問題。
- 路由器或防火墻啟用一些反IP欺騙的功能,保證對(duì)所有IP源可見。
- 增加對(duì)該服務(wù)的網(wǎng)絡(luò)和web流量監(jiān)控,時(shí)刻觀察變化。
針對(duì)正常的業(yè)務(wù)場(chǎng)景則考慮從高并發(fā)系統(tǒng)角度進(jìn)行不斷拓展優(yōu)化了,大抵可以從以下幾個(gè)角度考慮:
- 分布式:通過將服務(wù)拆分到不同的服務(wù)上構(gòu)成分布式架構(gòu),分散管理不同業(yè)務(wù)的流量壓力,進(jìn)行針對(duì)性的優(yōu)化,同時(shí)還能避免單功能導(dǎo)致服務(wù)宕機(jī)的影響面,也能提升系統(tǒng)的可伸縮性。
- 集群部署:通過集群部署分散單業(yè)務(wù)服務(wù)請(qǐng)求的流量,結(jié)合負(fù)載均衡技術(shù)提升服務(wù)的吞吐量和響應(yīng)速度,提升系統(tǒng)性能和可用性。
- 緩存:對(duì)于熱點(diǎn)數(shù)據(jù)提前緩存,提高程序讀寫性能和可靠性。
- 異步處理:采用異步處理機(jī)制,例如消息隊(duì)列、事件等系統(tǒng),減少響應(yīng)耗時(shí)、提升系統(tǒng)吞吐量。
- 資源預(yù)熱:將系統(tǒng)常見流量數(shù)據(jù)提前加載預(yù)熱,減少請(qǐng)求的等待時(shí)間。
- 程序優(yōu)化:針對(duì)每個(gè)功能代碼進(jìn)行功能優(yōu)化,例如:異步IO、檢索鎖粒度提升并發(fā)性能、減少循環(huán)遞歸,減少事務(wù)的粒度等。
- 數(shù)據(jù)庫(kù)優(yōu)化:合理設(shè)計(jì)數(shù)據(jù)表索引、字段,以及對(duì)于一些需要join查詢,我們可以結(jié)合場(chǎng)景進(jìn)行冗余。
- 讀寫分離:大部分場(chǎng)景都是讀多寫少,所以我們的數(shù)據(jù)庫(kù)一般建議采用讀寫分離的方式,將寫請(qǐng)求打到主庫(kù),讀取請(qǐng)求分散到讀服務(wù)器上,提升程序并發(fā)度和可擴(kuò)展性,同時(shí)即時(shí)主庫(kù)出現(xiàn)故障,從庫(kù)依然可以提供服務(wù)。
- 分庫(kù)分表:為避免慢查詢SQL和大量讀請(qǐng)求,我們建議采用分庫(kù)分表分散垂直和水平拆分分散請(qǐng)求流量和單表性能瓶頸。
- 通過使用限流、熔斷、降級(jí)等技術(shù),防止某個(gè)組件故障導(dǎo)致系統(tǒng)崩潰的雪崩效用。
- 監(jiān)控:針對(duì)各種可能存在的風(fēng)險(xiǎn)點(diǎn),通過監(jiān)控建議監(jiān)控觀察,以保證能夠及時(shí)針對(duì)做出調(diào)整和觀察。
3. 關(guān)于服務(wù)限流的探討
為什么針對(duì)流量需要進(jìn)行特定的服務(wù)限流,增加服務(wù)器承載更多的流量不是更好嗎?
原則上服務(wù)優(yōu)先,針對(duì)這個(gè)問題要從以下兩個(gè)角度考慮:
- 服務(wù)好客戶沒有錯(cuò),但是我們要知曉客戶是否是正常客戶,請(qǐng)求突增的原因不一定是業(yè)務(wù)量上漲,也可能是被攻擊了。
- 突發(fā)流量:出問題一般是由于突發(fā)流量,例如我們系統(tǒng)最高qps為1000,但是突發(fā)流量是遠(yuǎn)遠(yuǎn)高于這個(gè)數(shù)的,如果沒有提前預(yù)測(cè)不做限流,服務(wù)可能直接被打卦了。
限流本質(zhì)要做的就是自我保護(hù),是系統(tǒng)的最后一道防線,只有通過限流保證服務(wù)器正常,然后在針對(duì)性排查流量來源,針對(duì)性擴(kuò)容。
十、分布式微服務(wù)
1. 詳解分布式微服務(wù)架構(gòu)特點(diǎn)
很多讀者對(duì)于分布式和微服務(wù)兩個(gè)概念會(huì)有所混淆,實(shí)際上微服務(wù)相較于分布式架構(gòu)做了更精細(xì)的切割,通過拆分成精細(xì)的子模塊保證模塊之間的高內(nèi)聚低耦合,讓每個(gè)模塊獨(dú)立存在并拆解到不同團(tuán)隊(duì)進(jìn)行專門的維護(hù),各個(gè)模塊按照自己協(xié)定開發(fā)進(jìn)行開發(fā),公開各自的協(xié)議實(shí)現(xiàn)和標(biāo)準(zhǔn),某些熱點(diǎn)模塊也可以類似于分布式架構(gòu)一樣進(jìn)行水平拓展,保證高性能和高可用。
在部署架構(gòu)上,因?yàn)槲⒎?wù)拆分原則是分布式架構(gòu)有所不同,它拆分的目的還包含模塊間的解耦和團(tuán)隊(duì)自治維護(hù),所以進(jìn)行模塊部署的時(shí)候,多個(gè)模塊以容器化的方式部署在單臺(tái)服務(wù)器,也可以多個(gè)模塊部署上不同服務(wù)器上,相較于分布式架構(gòu)有著更加靈活的搭配和管理。
總的來說微服務(wù)架構(gòu)相較于分布式架構(gòu)有著更精細(xì)的拆分、有著更獨(dú)立的自治性和服務(wù)異構(gòu)性等特點(diǎn):
2. 服務(wù)拆分后的接口機(jī)器預(yù)估
微服務(wù)架構(gòu)通過更細(xì)粒度的拆分,使之業(yè)務(wù)維度有著更細(xì)粒度的解耦,所以對(duì)于特定服務(wù)的優(yōu)化,我們甚至可以是針對(duì)到更細(xì)粒度的核心接口上,例如我們現(xiàn)在有一個(gè)查詢接口,按照評(píng)估它大約是有5000QPS,經(jīng)過優(yōu)化后接口RT為大約是200ms,此時(shí)如果我們需要針對(duì)該模塊進(jìn)行水平拓展,請(qǐng)問需要增加幾臺(tái)服務(wù)器?
基于上述指標(biāo),我們首先需要針對(duì)單機(jī)性能進(jìn)行大體的預(yù)估,首先接口響應(yīng)實(shí)踐大約是200ms,按照單臺(tái)服務(wù)器單線程維護(hù)來看,每個(gè)線程在1s內(nèi)可以處理5個(gè)請(qǐng)求:
完成線程處理能力大體評(píng)估之后,我們就需要從處理的維度進(jìn)行更進(jìn)一步的評(píng)估,假設(shè)這個(gè)接口CPU處理時(shí)間為2ms,而IO等待耗時(shí)為150ms,其余時(shí)間作為網(wǎng)絡(luò)讀寫的耗時(shí)(讀寫redis數(shù)據(jù)等),這也就意味著一個(gè)線程在單位時(shí)間內(nèi)只占用CPU大約10ms,也就是說單個(gè)CPU大約可以處理100個(gè)線程,也就是每個(gè)核心在理想情況下,每秒可以處理500個(gè)請(qǐng)求。
我們繼續(xù),默認(rèn)情況下tomcat分配的線程是200,按照當(dāng)前CPU處理能力每秒可以處理100個(gè)線程,所以2個(gè)核心即可在單位時(shí)間內(nèi)處理掉這些線程,CPU使用率也不算很高,這個(gè)評(píng)估是合理的。 所以單機(jī)性能的核心計(jì)算還是回到了線程上,一共200個(gè)線程,每個(gè)處理5個(gè)請(qǐng)求,所以單機(jī)處理能力為1000QPS。
當(dāng)然如果我們?cè)试S更高的CPU使用率,就可以開始使用更多的線程更高的QPS。
所以在理想情況下3000qps只需要3臺(tái)服務(wù)器即可,但是我們的理論指標(biāo)是基于所有理想情況,并沒有考慮到應(yīng)用GC情況、網(wǎng)絡(luò)組件通信效率等綜合考量做增減。
3. 微服務(wù)界面長(zhǎng)耗時(shí)問題
通過微服務(wù)拆解,我們得到了更加靈活組合式實(shí)現(xiàn)高性能、高吞吐、高可用的軟件架構(gòu),假設(shè)線上出現(xiàn)用戶進(jìn)入緩慢,監(jiān)控服務(wù)器cpu和緩存沒有什么壓力,可以從哪些方面排查?
針對(duì)線上服務(wù)器多節(jié)點(diǎn)分布,但是在cpu和緩存沒有任何壓力瓶頸的情況下出現(xiàn)訪問速度緩慢,我們大體可以從以下幾個(gè)角度考慮:
- 明確是否是用戶端瀏覽器、網(wǎng)絡(luò)問題。
- 檢查服務(wù)器網(wǎng)絡(luò)帶寬等,明確是否存在網(wǎng)絡(luò)延遲丟包或者帶寬限制。
- 是否使用CDN,也有可能是CDN某個(gè)節(jié)點(diǎn)有問題導(dǎo)致用戶資源加載慢。
- 檢查復(fù)雜均衡是否正確,用戶請(qǐng)求是否打到有問題的節(jié)點(diǎn)上。
- 查看期間是否存在進(jìn)行耗時(shí)的GC。
- 查看應(yīng)用此時(shí)是否存在耗時(shí)的IO操作。