域名預(yù)訂/競(jìng)價(jià),好“米”不錯(cuò)過(guò)
互聯(lián)網(wǎng)發(fā)展至今各種應(yīng)用層出不窮,用戶(hù)量動(dòng)輒上億。所以如何構(gòu)建一個(gè)優(yōu)秀的高性能、高可靠的應(yīng)用系統(tǒng)對(duì)每一個(gè)開(kāi)發(fā)者至關(guān)重要。本文將我所學(xué)到和在工作中使用到的一些方法歸納總結(jié),希望給其他同學(xué)起到一些借鑒作用,在以后的開(kāi)發(fā)中遇到類(lèi)似的問(wèn)題,能快速的找到解決方案。本人主要使用語(yǔ)言是JAVA,所以下面不做特殊說(shuō)明,都是使用JAVA語(yǔ)言
高性能的關(guān)鍵
要想做到高性能,我總結(jié)了三點(diǎn):
緩存
DNS緩存
數(shù)據(jù)庫(kù)緩存
分布式緩存
拆分
業(yè)務(wù)拆分
數(shù)據(jù)庫(kù)拆分
異步
網(wǎng)絡(luò)異步
磁盤(pán)異步
使用消息
上面舉了一些三點(diǎn)中常見(jiàn)的情況,無(wú)論什么地方遇到性能瓶頸,謹(jǐn)記這三點(diǎn),大多數(shù)時(shí)候都能找到解決方案。以下分別介紹在整個(gè)架構(gòu)中各個(gè)方面對(duì)這三點(diǎn)的應(yīng)用
無(wú)狀態(tài)服務(wù)
說(shuō)無(wú)狀態(tài)服務(wù)我們首先要想到無(wú)狀態(tài)對(duì)象,無(wú)狀態(tài)對(duì)象簡(jiǎn)單的可以理解為沒(méi)有Field的對(duì)象,比如model/entity對(duì)象就不屬于無(wú)狀態(tài)對(duì)象,因?yàn)樗蠪ield,比如典型MVC場(chǎng)景的**Controller,**Service就是無(wú)狀態(tài)的,他們只含有method。有的也是有狀態(tài)的,比如Structs2框架的Action,所以Structs2現(xiàn)在用得比較少了。有了無(wú)狀態(tài)對(duì)象,我們才有可能構(gòu)建無(wú)狀態(tài)服務(wù),因?yàn)檎?qǐng)求鏈路中不包含有狀態(tài)對(duì)象,所以我們每一次請(qǐng)求都是獨(dú)立的,這樣的架構(gòu)有助于我們服務(wù)進(jìn)行擴(kuò)展。
無(wú)狀態(tài)服務(wù)有時(shí)候不可避免的會(huì)遇到一些有狀態(tài)的對(duì)象,比如最常見(jiàn)的就是session。因?yàn)閔ttp請(qǐng)求本身是無(wú)狀態(tài)的,所以必須cookie和session配合使用,才能識(shí)別多次http請(qǐng)求屬于同一用戶(hù)。一般有兩種方法解決:
使用cookie存儲(chǔ)
使用分布式session服務(wù)
第一種就是將對(duì)象信息全部存儲(chǔ)在cookie中,通過(guò)相應(yīng)的算法等在服務(wù)端將cookie中的信息讀出來(lái)。這些信息一般都會(huì)進(jìn)行加密處理。
第二種方法,就是將session存儲(chǔ)在分布式數(shù)據(jù)庫(kù)或者分布式緩存中,一般存在redis或者memcache中。那這種服務(wù)擴(kuò)展會(huì)依賴(lài)第三方數(shù)據(jù)庫(kù)或緩存的能力。淘寶有類(lèi)似的組件,開(kāi)源世界也有基于memcache和redis的分布式session
無(wú)狀態(tài)服務(wù)用到了拆分和緩存
業(yè)務(wù)拆分
無(wú)狀態(tài)可以使應(yīng)用服務(wù)水平擴(kuò)展,但是當(dāng)單個(gè)應(yīng)用太大太臃腫時(shí),有必要對(duì)應(yīng)用進(jìn)行拆分。垂直拆分即按業(yè)務(wù)拆分,比如電商系統(tǒng)中,按照訂單系統(tǒng),積分系統(tǒng)等進(jìn)行拆分。拆分可以方便開(kāi)發(fā),更方便擴(kuò)展。系統(tǒng)大了以后,每個(gè)業(yè)務(wù)的訪(fǎng)問(wèn)量是不一樣的,比如買(mǎi)家系統(tǒng)肯定比賣(mài)家系統(tǒng)訪(fǎng)問(wèn)量大得多,這時(shí)候就可以只增加買(mǎi)家系統(tǒng)的機(jī)器即可。
除了按照業(yè)務(wù)的不同拆分成不同的系統(tǒng)以外,針對(duì)我們的應(yīng)用分層也可以進(jìn)行拆分,一般分為應(yīng)用層、邏輯層和原子層。應(yīng)用層就是各種數(shù)據(jù)、邏輯業(yè)務(wù)的組裝,邏輯層含有大量可重用邏輯,原子層直接操作數(shù)據(jù)庫(kù),一些基本的數(shù)據(jù)操作包含在其中。
不論以何種形式拆分,拆分以后的系統(tǒng)在物理層面上就分離開(kāi)來(lái),所以系統(tǒng)間的通信是拆分中最重要的問(wèn)題所在。
RPC
在RPC服務(wù)之前已經(jīng)許多系統(tǒng)通信的方法,比如RMI、WebService,但是RPC以更方便,更高效,跨平臺(tái)的方式現(xiàn)在成為主流的通信手段。幾乎每個(gè)大公司都有自己的RPC框架:淘寶的HSF、58的SCF,也有非常多優(yōu)秀的開(kāi)源框架:Dubbo、GRPC、Thrift等等。國(guó)內(nèi)用dubbo的大公司也很多:京東、當(dāng)當(dāng)都是。
MQ
RPC調(diào)用一般是用在耦合比較重,同步調(diào)用的場(chǎng)景下。而MQ作為另一種異步通信的手段也被廣泛使用在各個(gè)業(yè)務(wù)中。常用的有:ActiveMQ、RabbitMQ、Kafka、RocketMQ。前兩個(gè)一般作為企業(yè)級(jí)應(yīng)用,主要特點(diǎn)是支持非常多的特性和規(guī)范。后兩者是互聯(lián)網(wǎng)級(jí)的,擁有更強(qiáng)力的吞吐和更高的性能,但是犧牲了很多MQ的特性。mq一般用在要求最終一直性即可的場(chǎng)景,比如用戶(hù)注冊(cè)和發(fā)積分這兩個(gè)動(dòng)作,可以用戶(hù)注冊(cè)以后直接返回前臺(tái)成功,然后發(fā)送注冊(cè)成功消息給mq系統(tǒng),發(fā)積分動(dòng)作訂閱注冊(cè)事件,消費(fèi)mq的事件信息。
MQ最大的好處就是削峰和解耦,在RPC式的同步調(diào)用場(chǎng)景中,如果同一個(gè)邏輯中調(diào)用A和B,那么在擴(kuò)展的時(shí)候,A和B一定是需要同時(shí)擴(kuò)展的,但是有了消息以后,A發(fā)送消息給B,及時(shí)B暫時(shí)處理不了,也可以等到A峰值過(guò)后B繼續(xù)處理,即使B短期無(wú)法匹配A的發(fā)送消息能力也沒(méi)有關(guān)系。
數(shù)據(jù)庫(kù)拆分
一般項(xiàng)目都會(huì)經(jīng)歷數(shù)據(jù)量從小到大的變化,所以數(shù)據(jù)庫(kù)拆分也是根據(jù)不同的數(shù)據(jù)量已經(jīng)不同的階段進(jìn)行相應(yīng)的處理。
讀寫(xiě)分離,這是大多數(shù)應(yīng)用在遇到性能瓶頸第一要干的事。大多數(shù)互聯(lián)網(wǎng)應(yīng)用都是讀占道90%以上的場(chǎng)景。所以一主多從,一個(gè)master做寫(xiě),其他slave做讀即可。但是這種主從模式也存在一些問(wèn)題,比如有一些數(shù)據(jù)需要及時(shí)性比較高,就是在寫(xiě)入以后馬上需要讀到。因?yàn)橹鲝耐绞峭ㄟ^(guò)log異步復(fù)制,所以存在數(shù)據(jù)不一致窗口,這個(gè)時(shí)候必須要通過(guò)強(qiáng)行讀取主庫(kù)來(lái)保證數(shù)據(jù)的安全,在開(kāi)發(fā)的時(shí)候一定要注意。
垂直分割,就是通過(guò)拆分將不同的業(yè)務(wù)放在不同的數(shù)據(jù)庫(kù)中,這樣就可以減少單一數(shù)據(jù)庫(kù)的壓力,提高整體性能。垂直分割要注意的是業(yè)務(wù)邊界問(wèn)題,邊界問(wèn)題就是有一個(gè)表,感覺(jué)放在A(yíng)中和放在B庫(kù)中都合適。這個(gè)就要靠經(jīng)驗(yàn)了,不能過(guò)分的考慮,因?yàn)槠鋵?shí)不論你在之前分得有多好,在應(yīng)用的迭代中,總會(huì)出現(xiàn)更多的找不到明確邊界的表。這個(gè)問(wèn)題在業(yè)務(wù)模塊劃分中也是一樣。
水平分割,一般就是說(shuō)sharding。將同一個(gè)表中的不同字段,拆分成不同的表,或者將同一張表按照hash或者業(yè)務(wù)字段分成不同的分片。這種一般需要DAL框架的支持,其中有TDDL、Cobar、Mycat等。主要就是通過(guò)框架讓程序編寫(xiě)者對(duì)數(shù)據(jù)庫(kù)的拆分不可見(jiàn),就像操作一個(gè)數(shù)據(jù)庫(kù)一樣。不過(guò)現(xiàn)在的DAL框架還不能達(dá)到這樣的目的,尤其是在跨庫(kù)事務(wù)的場(chǎng)景下,一般都需要其他方式處理。
跨庫(kù)事務(wù)/分布式事務(wù)
跨庫(kù)事務(wù)一般都是通過(guò)最終一致性來(lái)解決,即不強(qiáng)求ACID都能滿(mǎn)足,容許數(shù)據(jù)不一致的時(shí)間窗口,但是總會(huì)有一個(gè)時(shí)間點(diǎn)數(shù)據(jù)會(huì)到最終一致的狀態(tài)。解決方案非常的多,不過(guò)核心原理都是一樣,不外乎都是靠補(bǔ)償來(lái)完成的。
緩存的使用
計(jì)算機(jī)世界有一句名言:“計(jì)算機(jī)科學(xué)領(lǐng)域的任何問(wèn)題都可以通過(guò)增加一個(gè)間接的中間層來(lái)解決”。緩存就是一種中間層。
使用緩存的場(chǎng)景非常非常的多,幾乎到了你能想到的所有地方。這里我們講通常的數(shù)據(jù)庫(kù)數(shù)據(jù)緩存
緩存一般有兩種,local和remote,一般來(lái)說(shuō)使用一種緩存即可,因?yàn)榫彺骐m好,但是維護(hù)緩存的更新和刪除卻是一件非常麻煩得事。一般緩存可分為讀緩存(大多數(shù)場(chǎng)景)和寫(xiě)緩存(一般針對(duì)數(shù)據(jù)安全性比較低的場(chǎng)景)。
比如將數(shù)據(jù)庫(kù)中的數(shù)據(jù)讀出時(shí)同時(shí)寫(xiě)入緩存中,下一次讀數(shù)據(jù)的時(shí)候就可以直接讀取緩存中的數(shù)據(jù),從而大大減小數(shù)據(jù)庫(kù)的壓力,說(shuō)起來(lái)很簡(jiǎn)單,其實(shí)這也存在很多種的架構(gòu),每種架構(gòu)都有利弊,大家可以詳細(xì)去了解。
寫(xiě)緩存,就是先將數(shù)據(jù)寫(xiě)入緩存中,然后一段時(shí)間再持久化,這樣同樣會(huì)提高效率,這種方案的問(wèn)題在于如果這時(shí)候宕機(jī),部分?jǐn)?shù)據(jù)將會(huì)丟失,所以適用于數(shù)據(jù)安全性較低的場(chǎng)景。
緩存雖然速度快,除了維護(hù)更新較為麻煩的是,內(nèi)存也是較為昂貴的硬件,所以除了將熱點(diǎn)數(shù)據(jù)存儲(chǔ)在緩存中,一般緩存中維護(hù)數(shù)據(jù)的索引或者主要字段用于列表顯示,真正的大而全的數(shù)據(jù)還需要其他方法解決。
靜態(tài)化
對(duì)于大多數(shù)場(chǎng)景,我們的數(shù)據(jù)在一定時(shí)間都是不會(huì)變化的,或者說(shuō)即使變化,也只是頁(yè)面的一小部分會(huì)發(fā)生變化,可以將不變化的部分單獨(dú)拿出來(lái)做靜態(tài)化。比如京東商城的頁(yè)面就是靜態(tài)化的,靜態(tài)化以后,數(shù)據(jù)不用每次都從緩存或者數(shù)據(jù)庫(kù)中取得,然后再封裝成頁(yè)面,而是直接請(qǐng)求返回靜態(tài)頁(yè)面,性能無(wú)疑提升了非常大。
除了以上常用的方法外,還要非常多的重要的方法:
CDN加速
DNS緩存
頁(yè)面緩存
使用分布式存儲(chǔ)
使用多線(xiàn)程編寫(xiě)程序
申請(qǐng)創(chuàng)業(yè)報(bào)道,分享創(chuàng)業(yè)好點(diǎn)子。點(diǎn)擊此處,共同探討創(chuàng)業(yè)新機(jī)遇!