8 張圖帶你了解大型應(yīng)用架構(gòu)演進(jìn)歷程
幾乎所有的大型應(yīng)用都是從一個(gè)小應(yīng)用開(kāi)始的,好的互聯(lián)網(wǎng)產(chǎn)品是慢慢運(yùn)營(yíng)出來(lái)的,不是一開(kāi)始就開(kāi)發(fā)好的,所以本篇我們來(lái)聊聊應(yīng)用架構(gòu)的演進(jìn)歷程。
如何打造一個(gè)高可用,高性能,易擴(kuò)展的應(yīng)用?首先我們了解一下大型應(yīng)用的特點(diǎn):
-
高可用:系統(tǒng)需要不間斷的提供服務(wù),不能出現(xiàn)單點(diǎn)故障
-
高并發(fā):在大流量的沖擊下,系統(tǒng)依然穩(wěn)定提供服務(wù)
-
大數(shù)據(jù):應(yīng)用每天都會(huì)產(chǎn)生大量的數(shù)據(jù),需要存儲(chǔ)和管理好這些數(shù)據(jù)
最簡(jiǎn)單的架構(gòu)
剛開(kāi)始應(yīng)用沒(méi)有太多訪問(wèn)量,所以只需要一臺(tái)服務(wù)器,這時(shí)候的架構(gòu)如下圖:
應(yīng)用程序、文件、數(shù)據(jù)庫(kù)往往都部署在一臺(tái)服務(wù)器上。應(yīng)用程序可以采用Java開(kāi)發(fā),部署在Tomcat服務(wù)器上,數(shù)據(jù)庫(kù)可以使用開(kāi)源的MySQL
應(yīng)用與數(shù)據(jù)服務(wù)分隔
隨著應(yīng)用的業(yè)務(wù)越來(lái)越復(fù)雜,應(yīng)用訪問(wèn)量越來(lái)越大,導(dǎo)致性能越來(lái)越差,存儲(chǔ)空間嚴(yán)重不足,這時(shí)候我們考慮把服務(wù)增加到三臺(tái)(能通過(guò)加機(jī)器解決的問(wèn)題都不是問(wèn)題);分離出應(yīng)用服務(wù)器、數(shù)據(jù)庫(kù)服務(wù)器、文件服務(wù)器。
-
應(yīng)用服務(wù)器需要處理大量的訪問(wèn),所以需要性能更好的CPU
-
數(shù)據(jù)庫(kù)服務(wù)器需要存儲(chǔ)大量的數(shù)據(jù)以及快速的檢索,所以需磁盤的檢索速度較快以及存儲(chǔ)空間大
-
文件服務(wù)器需要存儲(chǔ)上傳的文件,需要更大的磁盤;現(xiàn)在通常情況下會(huì)選擇第三方的存儲(chǔ)服務(wù)
根據(jù)每個(gè)服務(wù)器對(duì)應(yīng)的場(chǎng)景,配置服務(wù)器后應(yīng)用的性能能夠大大提高,更好的支持業(yè)務(wù)的發(fā)展。但是隨之業(yè)務(wù)的發(fā)展,訪問(wèn)量的增大,這種架構(gòu)又將再次面臨挑戰(zhàn),應(yīng)用服務(wù)器處理能力下降,存儲(chǔ)空間不足
應(yīng)用服務(wù)器集群
在高并發(fā),大流量的情況下,一臺(tái)服務(wù)器是肯定處理不過(guò)來(lái)的,這個(gè)時(shí)候增加服務(wù)器,部署集群提供服務(wù),來(lái)分擔(dān)每臺(tái)服務(wù)器的壓力。部署集群的另一個(gè)好處是可伸縮性,比如當(dāng)遇到了雙11大流量的場(chǎng)景下,可以增加服務(wù)器分?jǐn)偭髁?,等雙11過(guò)后,減少服務(wù)器節(jié)約成本。架構(gòu)如下:
如果應(yīng)用服務(wù)器是Tomcat,那么可以部署一個(gè)Tomcat的集群,外部在部署一個(gè)負(fù)載均衡器,可以采用隨機(jī)、輪詢或者一致性哈希算法達(dá)將用戶的請(qǐng)求分發(fā)到不同應(yīng)用服務(wù)集群;通常選擇的免費(fèi)的負(fù)載均衡是nginx。在這種架構(gòu)下,應(yīng)用服務(wù)器的負(fù)載將不會(huì)是整個(gè)應(yīng)用的瓶頸點(diǎn);
雖然應(yīng)用程序的處理速度在這種架構(gòu)下提升了許多,但是又會(huì)暴露一個(gè)問(wèn)題,數(shù)據(jù)庫(kù)的壓力大大增大,導(dǎo)致訪問(wèn)響應(yīng)延遲,影響整個(gè)應(yīng)用的性能。這種架構(gòu)還有個(gè)問(wèn)題,通常應(yīng)用是有狀態(tài)的,需要記錄用戶的登錄信息,如果每次用戶的請(qǐng)求都是隨機(jī)路由到后端的應(yīng)用服務(wù)器,那么用戶的會(huì)話將會(huì)丟失;解決這個(gè)問(wèn)題兩個(gè)方案:
-
采用一致性hash把用戶的請(qǐng)求路由到同一個(gè)Tomcat,如果有一臺(tái)服務(wù)器跪了,那么這臺(tái)服務(wù)器上面的用戶信息將會(huì)丟失
-
Tomcat集群之間通過(guò)配置session復(fù)制,達(dá)到共享,此方案效率較低
兩個(gè)方案都不是很好,那么還有其他的方案嗎?請(qǐng)繼續(xù)往下看
根據(jù)二八原則,80%的的業(yè)務(wù)都是集中訪問(wèn)20%的數(shù)據(jù),這20%的數(shù)據(jù)通常稱為熱點(diǎn)數(shù)據(jù),但是這20%的數(shù)據(jù)占用的內(nèi)存也不會(huì)小,如果每個(gè)應(yīng)用服務(wù)器都存放一份,有些浪費(fèi)存儲(chǔ)空間,所以這時(shí)候需要考慮加入分布式緩存服務(wù)器(常用的是Redis);當(dāng)引入了分布式緩存服務(wù)器,再來(lái)看上面那個(gè)方案的問(wèn)題,就可以解決了,把用戶的會(huì)話存放到緩存服務(wù)器,不僅可以防止用戶數(shù)據(jù)丟失,效率也不低;架構(gòu)圖如下:
由于分布式緩存服務(wù)器畢竟存放在遠(yuǎn)程,需要經(jīng)過(guò)網(wǎng)絡(luò),所以取數(shù)據(jù)還是要花一點(diǎn)時(shí)間;本地緩存訪問(wèn)速度更快,但是內(nèi)存空間有限,并且還會(huì)出現(xiàn)和應(yīng)用程序爭(zhēng)搶資源;所以這種架構(gòu)搭配了分布式緩存和本地緩存,本地緩存存放少量常用熱點(diǎn)數(shù)據(jù),當(dāng)本地緩存中沒(méi)有命中時(shí)在去集中式緩存取
在引進(jìn)緩存之后,數(shù)據(jù)庫(kù)的訪問(wèn)壓力可以的一定的緩解
數(shù)據(jù)庫(kù)讀寫分離
雖然在加入了緩存之后,部分?jǐn)?shù)據(jù)可以直接走緩存,不需要訪問(wèn)數(shù)據(jù)庫(kù),但是任然會(huì)有一些請(qǐng)求,會(huì)訪問(wèn)數(shù)據(jù)庫(kù),比如:緩存失效,緩存未命中;當(dāng)流量大的時(shí)候,數(shù)據(jù)庫(kù)的訪問(wèn)量也不小。這時(shí)候我們需要考慮搭建數(shù)據(jù)庫(kù)集群,讀寫分離
當(dāng)應(yīng)用服務(wù)器有寫操作時(shí),訪問(wèn)主庫(kù),當(dāng)應(yīng)用程序有讀操作時(shí),訪問(wèn)從庫(kù);大多數(shù)的應(yīng)用都是讀的操作遠(yuǎn)遠(yuǎn)大于寫的操作,所以可以配置數(shù)據(jù)庫(kù)一主多從來(lái)分擔(dān)數(shù)據(jù)庫(kù)的壓力;為了讓應(yīng)用程序?qū)?yīng)主庫(kù)和從庫(kù)無(wú)感知,通常需要引入一些讀寫分離的框架做一個(gè)統(tǒng)一的數(shù)據(jù)訪問(wèn)模塊。
這種架構(gòu)通常需要警惕的一個(gè)問(wèn)題是主從延遲,當(dāng)在高并發(fā)的場(chǎng)景下,主庫(kù)剛寫成功,數(shù)據(jù)庫(kù)還未成功同步完從庫(kù),這時(shí)候另一個(gè)請(qǐng)求進(jìn)入讀取數(shù)據(jù)發(fā)現(xiàn)不存在;解放方案是在應(yīng)用程序中高并發(fā)的場(chǎng)景下設(shè)置強(qiáng)制走主庫(kù)查詢
❝
兄弟們,請(qǐng)不要白嫖哦,文章看一半,請(qǐng)先點(diǎn)個(gè)贊
❞
反向代理和CDN
假如隨著業(yè)務(wù)的不斷擴(kuò)大,全國(guó)各地都會(huì)使用到我們的應(yīng)用,由于各地區(qū)的網(wǎng)絡(luò)情況不同,所以有的人請(qǐng)求響應(yīng)速度快,有的人請(qǐng)求響應(yīng)速度慢,這會(huì)嚴(yán)重的影響到用戶的體驗(yàn)。為了提高響應(yīng)速度需要引入反向代理和CDN;CDN和反向代理都是采用的緩存,目的:
-
盡可能快的把數(shù)據(jù)呈現(xiàn)給用戶
-
減輕后端服務(wù)器的壓力
架構(gòu)圖如下:
CDN: 部署在網(wǎng)絡(luò)提供商的機(jī)房,當(dāng)用戶來(lái)訪問(wèn)的時(shí)候,從距離用戶最近的服務(wù)器返回?cái)?shù)據(jù),盡快呈現(xiàn)給用戶;通常情況下在CDN中緩存的是靜態(tài)資源(html,js,css),達(dá)到動(dòng)靜分離;但是有時(shí)候遇到了某些數(shù)據(jù)訪問(wèn)量特別大的時(shí)候,后端會(huì)生成靜態(tài)資源放入到CDN,比如:商城的首頁(yè),每個(gè)用戶進(jìn)入都需要訪問(wèn)的頁(yè)面,如果每次請(qǐng)求都進(jìn)入到后端,那么服務(wù)器的壓力肯定不小,這種情況下會(huì)把首頁(yè)生成靜態(tài)的文件緩存到cdn和反向代理服務(wù)器
反向代理:部署在應(yīng)用的中心機(jī)房,通常也是緩存的靜態(tài)資源,當(dāng)用戶通過(guò)CDN未請(qǐng)求到需要的數(shù)據(jù)時(shí),先進(jìn)入反向代理服務(wù)器,如果有緩存用戶訪問(wèn)的數(shù)據(jù),那么直接返回給用戶;這里也有特殊情況,對(duì)于有些場(chǎng)景下的熱點(diǎn)數(shù)據(jù),在這里根據(jù)用戶的請(qǐng)求去分布式緩存服務(wù)器中獲取,能拿到就直接返回。
這種架構(gòu)已經(jīng)把緩存做到了4級(jí)
-
第一級(jí):CDN 緩存靜態(tài)資源
-
第二級(jí):反向代理緩存靜態(tài)資源以及部分熱點(diǎn)數(shù)據(jù)
-
第三級(jí):應(yīng)用服務(wù)器的本地緩存
-
第四級(jí):分布式緩存服務(wù)器
通常情況下經(jīng)過(guò)了這4級(jí)緩存,能夠進(jìn)入到數(shù)據(jù)庫(kù)的請(qǐng)求也不多了,很好的釋放了數(shù)據(jù)庫(kù)的壓力
搜索引擎和NoSQL
隨著業(yè)務(wù)的不斷擴(kuò)大,對(duì)于數(shù)據(jù)的存儲(chǔ)和查詢的需求也越來(lái)越復(fù)雜,通常情況我們需要引入非關(guān)系型數(shù)據(jù)庫(kù),比如搜索引擎和NoSQL數(shù)據(jù)庫(kù)
有時(shí)候我們的查詢場(chǎng)景很復(fù)雜,需要查詢很多數(shù)據(jù)表,經(jīng)過(guò)一系列的計(jì)算才能完成,這時(shí)候可以考慮通過(guò)數(shù)據(jù)同步工具(比如canal)拉去數(shù)據(jù)到大數(shù)據(jù)平臺(tái),使用批處理框架離線計(jì)算,把輸出的結(jié)果存放到搜索引擎或者NoSQL數(shù)據(jù)庫(kù)中,應(yīng)用程序直接查詢計(jì)算的結(jié)果返回給用戶。也有可能我們需要匯總多個(gè)表的數(shù)據(jù)做一張寬表,方便應(yīng)用程序查詢
由于引入的數(shù)據(jù)存儲(chǔ)方式增多,為了減輕應(yīng)用程序的管理多個(gè)數(shù)據(jù)源的麻煩,需要封裝統(tǒng)一數(shù)據(jù)訪問(wèn)模塊,如果使用的時(shí)Java,可以考慮spring-data
業(yè)務(wù)縱向拆分
互聯(lián)網(wǎng)公司通常的宗旨是小步迭代試錯(cuò)快跑,當(dāng)業(yè)務(wù)發(fā)展到足夠大,對(duì)于單體應(yīng)用想要達(dá)到這個(gè)宗旨是有難度的,隨著業(yè)務(wù)的發(fā)展,應(yīng)用程序越來(lái)越大,研發(fā)、維護(hù)、發(fā)布的成本也越來(lái)越大,這時(shí)候就需要考慮根據(jù)業(yè)務(wù)把單體應(yīng)用拆分為多個(gè)服務(wù),服務(wù)之間可以通過(guò)RPC遠(yuǎn)程調(diào)用和消息隊(duì)列來(lái)一起完成用戶的請(qǐng)求。
由于業(yè)務(wù)的拆分,通常情況下也會(huì)相應(yīng)的對(duì)數(shù)據(jù)庫(kù)進(jìn)行拆分,達(dá)到一個(gè)服務(wù)對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)的理想狀態(tài)
引入MQ的好處:
-
提高系統(tǒng)的可用性:當(dāng)消費(fèi)服務(wù)器發(fā)送故障時(shí),消息還在消息隊(duì)列中,數(shù)據(jù)不會(huì)丟失
-
加快請(qǐng)求的響應(yīng):當(dāng)用戶請(qǐng)求到達(dá)服務(wù)器后,把請(qǐng)求中可以異步處理的數(shù)據(jù)放入到MQ,讓系統(tǒng)逐一消費(fèi),不需要用戶等待,加快了響應(yīng)速度
-
削峰填谷:當(dāng)大量請(qǐng)求都同時(shí)進(jìn)入到系統(tǒng)之后,會(huì)全部放入到消息隊(duì)列,系統(tǒng)逐一消費(fèi),不會(huì)對(duì)系統(tǒng)造成很大的沖擊
還有一個(gè)情況未談及到,就是數(shù)據(jù)庫(kù)的水平拆分,這也是數(shù)據(jù)庫(kù)拆分的最后手段,只有當(dāng)單表數(shù)據(jù)特別大,不能滿足業(yè)務(wù)的需要才使用。使用最多的還是進(jìn)行數(shù)據(jù)庫(kù)的業(yè)務(wù)縱向拆分,把數(shù)據(jù)庫(kù)中不同業(yè)務(wù)的數(shù)據(jù)放到不同的物理服務(wù)器上。
應(yīng)用當(dāng)前到底選擇什么架構(gòu),一定要根據(jù)實(shí)際業(yè)務(wù)的需求進(jìn)行靈活的選擇,驅(qū)動(dòng)技術(shù)架構(gòu)發(fā)展的主要?jiǎng)恿€是在于業(yè)務(wù)的發(fā)展,不要為了技術(shù)而技術(shù)。
寫在最后
-
首先感謝大家可以耐心地讀到這里。
-
當(dāng)然,文中或許會(huì)存在或多或少的不足、錯(cuò)誤之處,有建議或者意見(jiàn)也非常歡迎大家在評(píng)論交流。