蚂蚁变大象:浅谈常规网站是如何从小变大的(转)
2005年,我開始和朋友們開始拉活兒做網(wǎng)站,當(dāng)時第一個網(wǎng)站是在linux上用jsp搭建的,到后來逐步的引入了多種框架,如webwork、hibernate等。在到后來,進(jìn)入公司,開始用c/c++,做分布式計算和存儲。(到那時才解開了我的一個疑惑:C語言除了用來寫HelloWorld,還能干嘛?^_^)。
總而言之,網(wǎng)站根據(jù)不同的需求,不同的請求壓力,不同的業(yè)務(wù)模型,需要不同的架構(gòu)來給予支持。我從我的一些經(jīng)歷和感受出發(fā),大體上總結(jié)了一下的一些階段。詳情容我慢慢道來。
?
【第一階段?:?搭建屬于自己的網(wǎng)站】
我們最先開始的網(wǎng)站可能是長成這個樣子的:
拿Java做例子,我們可能會引入struts、spring、hibernate等框架,用來做URL分流,C、V、M隔離,數(shù)據(jù)的ORM等。這樣,我們的系統(tǒng)中,數(shù)據(jù)訪問層可以抽取出很多公用的類,業(yè)務(wù)邏輯層也可以抽取出很多公用的業(yè)務(wù)類,同一個業(yè)務(wù)邏輯可以對應(yīng)多個展示頁面,可復(fù)用性得到極大的增強。
不過,從性能上看,引入框架后,效率并不見得比第一種架構(gòu)高,有可能還有降低。因為框架可能會大量引入“反射”的機制,來創(chuàng)建對應(yīng)的業(yè)務(wù)對象;同時,也可能增加額外的框架邏輯,來增強隔離性。從而使得整體服務(wù)能力下降。幸好,在這個階段,業(yè)務(wù)請求量不大,性能不是我們太care的事情。J
?
【第三階段?:降低磁盤壓力?】
可能隨著業(yè)務(wù)的持續(xù)發(fā)展,或者是網(wǎng)站關(guān)注度逐步提升(也有可能是搜索引擎的爬蟲關(guān)注度逐步提升。我之前有一個網(wǎng)站,每天有超過1/3的訪問量,就是各種爬蟲貢獻(xiàn)的),我們的請求量逐步變大,這個時候,往往出現(xiàn)瓶頸的就是磁盤性能。在linux下,用vmstat、iostat等命令,可以看到磁盤的bi、bo、wait、util等值持續(xù)高位運行。怎么辦呢?
其實,在我們剛剛踏進(jìn)大學(xué)校門的時候,第一門計算機課程——《計算機導(dǎo)論》里面就給出了解決方案。依稀記得下面這個圖:
在我們的存儲體系里面,磁盤一般是機械的(現(xiàn)在Flash、SSD等開始逐步大規(guī)模使用了),讀取速度最慢,而內(nèi)存訪問速度較快(讀取一個字節(jié)約10μs,速度較磁盤能高幾百倍),速度最快的是CPU的cache。不過價格和存儲空間卻遞減。
話題切換回來,當(dāng)我們的磁盤出現(xiàn)性能瓶頸的時候,我們這個時候,就要考慮其他的存儲介質(zhì),那么我們是用cpu cache還是內(nèi)存呢,或是其他形態(tài)的磁盤?綜合性價比來看,到這個階段,我個人還是推薦使用內(nèi)存。現(xiàn)在內(nèi)存真是白菜價,而且容量持續(xù)增長(我現(xiàn)在就看到64G內(nèi)存的機器[截止2012-4-3])。
但是問題來了,磁盤是持久化存儲的,斷電后。數(shù)據(jù)不會丟失,而內(nèi)存卻是易失性存儲介質(zhì),斷電后內(nèi)容會丟失。因此,內(nèi)存只能用來保存臨時性數(shù)據(jù),持久性數(shù)據(jù)還是需要放到磁盤等持久化介質(zhì)上。因此,內(nèi)存可以有多種設(shè)計,其中最常見的就是cache(其他的設(shè)計方式會在后面提及)。這種數(shù)據(jù)結(jié)構(gòu)通常利用LRU算法(現(xiàn)在還有結(jié)合隊列、集合等多種數(shù)據(jù)結(jié)構(gòu),以及排序等多種算法的cache),用于記錄一段時間的臨時性數(shù)據(jù),在必要的時候可以淘汰或定期刪除,以保證數(shù)據(jù)的有效性。cache通常以Key-Value形式來存儲數(shù)據(jù)(也有Key-SubKey-Value,或者是Key-List,以及Key-Set等形式的)。因為數(shù)據(jù)存放在內(nèi)存,所以訪問速度會提高上百倍,并且極大的減少磁盤IO壓力。
Cache有多種架構(gòu)設(shè)計,最常見的就是穿透式和旁路式。穿透式通常是程序本身使用對應(yīng)的cache代碼庫,將cache編譯進(jìn)程序,通過函數(shù)直接訪問。旁路式則是以服務(wù)的方式提供查詢和更新。在此階段,我們通常使用旁路式cache,這種cache往往利用開源的服務(wù)程序直接搭建就可以使用(如MemCache)。旁路式結(jié)構(gòu)如下圖:
請求來臨的時候,我們的程序先從cache里面取數(shù)據(jù),如果數(shù)據(jù)存在并且有效,就直接返回結(jié)果;如果數(shù)據(jù)不存在,則從數(shù)據(jù)庫里面獲取,經(jīng)過邏輯處理后,先寫入到cache,然后再返回給用戶數(shù)據(jù)。這樣,我們下次再訪問的時候,就可以從cache中獲取數(shù)據(jù)。
Cache引入以后,最重要的就是調(diào)整內(nèi)存的大小,以保證有足夠的命中率。根據(jù)經(jīng)驗,好的內(nèi)存設(shè)置,可以極大的提升命中率,從而提升服務(wù)的響應(yīng)速度。如果原來IO有瓶頸的網(wǎng)站,經(jīng)過引入內(nèi)存cache以后,性能提升10倍應(yīng)該是沒有問題的。
不過,有一個問題就是:cache依賴。如果cache出問題(比如掛了,或是命中率下降),那就杯具了L。這個時候,服務(wù)就會直接將大的壓力壓向數(shù)據(jù)庫,造成服務(wù)響應(yīng)慢,或者是直接500。
另外,服務(wù)如果重新啟動時,也會出現(xiàn)慢啟動,即:給cache充數(shù)據(jù)的階段。對于這種情況,可以采取回放日志,或是從數(shù)據(jù)庫抽取最新數(shù)據(jù)等方式,在服務(wù)啟動前,提前將一部分?jǐn)?shù)據(jù)放入到cache中,保證有一定命中率。
通過這樣的拆分后,我們就邁出了多機的第一步。雖然看起來比較簡單和容易,但是這也是非常具有里程碑意義的。這樣的優(yōu)化,可能會提升20-30%左右的一個CPU idle。能夠使得我們的網(wǎng)站能夠經(jīng)受更大的壓力。
?
【第五階段?:?邏輯程序的多機化】
當(dāng)我們的訪問量持續(xù)增加的時候,我們承受這成長的快樂和痛苦。流量刷刷往上漲,高興!但是呢,服務(wù)器叫苦不絕,我們的程序已經(jīng)快到不能服務(wù)的邊緣了。怎么辦?
“快使用分布式,哼哼哈嘿”J
這個時候,我們就需要針對CPU瓶頸,將我們的程序分別放在多臺服務(wù)器上,讓他們同時提供服務(wù),將用戶請求分?jǐn)偟蕉鄠€提供服務(wù)的機器。
好,如果提供這樣的服務(wù),我們會遇到什么樣的問題?怎么樣來解決?
我們一個個的來分析:
一、WebServer怎么樣來分流用戶請求?That’s a good question!
在考慮這個問題的時候,我們常見的WebServer早已給我們想好了解決方案。現(xiàn)在主流的WebServer幾乎都提供一個叫“Load Balance”的功能,翻譯過來就是負(fù)載均衡。我們可以在WebServer上配置一組機器列表,當(dāng)請求來臨的時候,WebServer會根據(jù)一定的規(guī)則選取某一臺機器,將請求轉(zhuǎn)發(fā)到對應(yīng)的邏輯處理程序上。
有同學(xué)馬上就會問了,“一定的規(guī)則”是怎么樣的規(guī)則?他能解決什么樣的問題?如果機器宕了怎么辦?……
哈哈,這里的一定規(guī)則就是“Load Balance”。負(fù)載均衡其實是分布式計算和存儲中最基礎(chǔ)的算法,他的好壞,直接決定了服務(wù)的穩(wěn)定性。我曾經(jīng)設(shè)計和開發(fā)了一個負(fù)載均衡算法(現(xiàn)在正在大規(guī)模使用),有一次就因為一個很小的case,導(dǎo)致服務(wù)大面積出現(xiàn)問題。
負(fù)載均衡要解決的就是,上游程序如何在我們提供的一堆機器列表中,找到合適的機器來提供下游的服務(wù)。因此,我們可以將負(fù)載均衡分成兩個方向來看:第一,根據(jù)怎樣的規(guī)則來選機器;第二,符合規(guī)則的機器中,哪些是能提供服務(wù)的。對于第一個問題,我們通常使用隨機、輪詢、一致Hash等算法;對于第二個問題,我們要使用心跳、服務(wù)響應(yīng)判定等方法檢測機器的健康狀態(tài)。關(guān)于負(fù)載均衡,要談的話點其實很多,我之前也寫過專門的一篇文章來介紹,后續(xù)有空了,我再詳細(xì)的描述。
總之,分流的問題,我們可以通過負(fù)載均衡來比較輕松的解決了。
二、用戶的session如何來同步?That’s a good question,TOO!
雖然HTTP協(xié)議是一個無狀態(tài)的服務(wù)協(xié)議,但是,用戶的基本信息是要求能夠保證的。比如:登錄信息。原來在單機的時候,我們可以很簡單的使用類似setSession(“user”, ”XXX”)的函數(shù)來解決。當(dāng)使用多機的時候,該怎么樣來解決呢?
其實這個問題也是當(dāng)年困擾我很久的一個問題。如果用setSession,用戶在某一臺機器上登錄了,當(dāng)下次請求來的時候,到其他機器了,就變成未登錄了。Oh,My God!
Ok,讓我們一個個的來看:
1、一臺機器登錄,其他機器不知道;
2、用戶請求可能到多臺機器。
對于第一個問題,如果我們在一臺機器的登錄信息讓其他機器知道,不就OK了嘛。或者,大家都在一臺機器上登錄,不就可以了嘛。?????對于第二個問題,如果我們讓同一個用戶的請求,只落在同一臺機器上,不就OK了嘛。因此,我們可以提出三種解決方案:
1、提供session同步機制;
2、提供統(tǒng)一session服務(wù);
3、將同一用戶分流到同一機器。
嗯,這三種方式,你會選哪個呢?如果是我,我就選最后一個。因為我是一個懶蟲,我會選最簡單的一個,我信奉的一個原則既是:簡單粗暴有效!哈哈。在WebServer層使用一致Hash算法,按session_id進(jìn)行分流(如果WebServer沒有提供該功能,可以簡單寫一個擴(kuò)展,或者干脆在WebServer后面做一個代理即可)。但是這種方案有一個致命的問題,當(dāng)一臺機器宕機了以后,該機器上的所有用戶的session信息即會丟失,即使是做了磁盤備份,也會有一段時間出現(xiàn)session失效。
好,那看看第一種方案。其實現(xiàn)在有一些框架已經(jīng)提供了這樣的服務(wù)機制。比如Tomcat就提供session同步機制。利用自有的協(xié)議,將一臺機器上的session數(shù)據(jù)同步到其他的機器上。這樣就有一個問題,我需要在所有的機器上配置需要同步的機器,機器的耦合度瞬間就增加了,煩啊!而且,如果session量比較大的話,同步的實效性還是一個問題。
那再來看看第二種方案,提供統(tǒng)一session服務(wù)。這個就是單獨再寫一個邏輯程序,來管理session,并且以網(wǎng)絡(luò)服務(wù)的方式提供查詢和更新。對于這樣的一個階段的服務(wù)來講,顯得重了一些。因為,我們?nèi)绻@樣做,又會面臨一堆其他的問題,比如:這個服務(wù)是否存在單點(一臺服務(wù)器,如果宕機服務(wù)就停止),使用什么樣的協(xié)議來進(jìn)行交互等等。這些問題在我們這個階段都還得不到解決。所以,看起來這個方案也不是很完美。
好吧,三種方案選其一,如果是你,你會選哪一種呢?或者還有更好的方案?如果我沒錢沒實力(傳說中的“屌絲”,哈哈),我就可能犧牲一下服務(wù)的穩(wěn)定性,采用代價最低的。
三、數(shù)據(jù)訪問同步問題。
當(dāng)多個請求同時到達(dá),并且競爭同一資源的時候(比如:秒殺,或是定火車票),我們怎么來解決呢?
這個時候,因為我們用到了單機數(shù)據(jù)庫,可以很好的利用數(shù)據(jù)庫的“鎖”功能來解決這個問題。一般的數(shù)據(jù)庫都提供事務(wù)的功能。事務(wù)的級別分多種,比如可重復(fù)讀、串行化等,根據(jù)不同的業(yè)務(wù)需求,可能會選擇不同的事務(wù)級別。我們可以在需要競爭的資源上加上鎖,用于同步資源的請求。但是,這個東東也不是萬能的,鎖會極大的影響效率,所以盡量的減少鎖的使用,并且已經(jīng)使用鎖的地方盡量的優(yōu)化,并檢查是否可能出現(xiàn)死鎖。
cache也有對應(yīng)的解決方案,比如延遲刪除或者凍結(jié)時間等技術(shù),就是讓資源在一段時間處于不可讀狀態(tài),用戶直接從數(shù)據(jù)庫查詢,這樣保證數(shù)據(jù)的有效性。
好了,上述三個問題,應(yīng)該涵蓋了我們在這個階段遇到的大部分問題。那么,我們現(xiàn)在可以把整體的架構(gòu)圖畫出來看看。
這樣的結(jié)構(gòu),足夠我們撐一段時間了,并且因為邏輯程序的無狀態(tài)性,可以通過增加機器來擴(kuò)展。而接下來我們要面對的,就是提交增長和查詢量增加帶來的存儲性能的瓶頸。
?
【第六階段?:?讀寫分離,提升IO性能】
好了,到現(xiàn)在這個階段,我們的單機數(shù)據(jù)庫可能已經(jīng)逐步成為瓶頸,數(shù)據(jù)庫出現(xiàn)比較嚴(yán)重的讀寫沖突(即:多個線程或進(jìn)程因為讀寫需要,爭搶磁盤,使得磁盤的磁頭不斷變換磁道或盤片,造成讀寫都很緩慢)。
那我們針對這樣的問題,看看有哪些方法來解決。
一、減少讀取量。我們所有的問題來源就是因為讀寫量增加,所以看起來這個是最直接最根源的解決辦法。不過,用戶有那么大請求量我們怎么可能減少呢?其實,對于越后端的系統(tǒng),這是越可能的事情。我們可以在每一層都減少一部分往后傳輸?shù)恼埱蟆>唧w到數(shù)據(jù)庫的話,我們可以考慮通過增加cache命中率,減少數(shù)據(jù)庫壓力。增加cache命中率有很多中方法,比如對業(yè)務(wù)訪問模式進(jìn)行優(yōu)化、多級cache模式、增加內(nèi)存容量等等。業(yè)務(wù)模式的修改不是太好通用,因此這里我們考慮如何通過增加內(nèi)存容量來解決問題。
對于單機,現(xiàn)在通用的cache服務(wù)一般都可以配置內(nèi)存大小,這個只需要很簡單的配置即可。另外,我們也可以考慮多機cache的方案,通過增加機器來擴(kuò)充內(nèi)存容量。因此,我們就引入了分布式cache,現(xiàn)在常用的cache(如:memcache),都帶有這樣的功能,支持多機cache服務(wù),可以通過負(fù)載均衡算法,將請求分散到多臺不同的機器上,從而擴(kuò)充內(nèi)存容量。
這里要強調(diào)一點。在我們選擇均衡算法的時候,是有考慮的。這個時候,常常選賊一致Hash算法,將某一系列ID分配到固定的機器,這樣的話,能放的KV對基本等于所有機器相加。否則,如果不做這樣的分配,所有機器內(nèi)存里面的內(nèi)容會有大量重復(fù),內(nèi)存并沒有很好的利用。另外,因為采用一致Hash,即使一臺機器宕掉,也會比較均勻的分散到其他機器,不會造成瞬間其他機器cache大量失效或不命中的問題。
二、減少寫入量。要減少用戶的提交,這個看起來是不太現(xiàn)實的。確實,我們要減少寫入的量似乎是很難的一件事。不過也不是完全不可能。這里我們會提到一個思想:合并寫入。就是將有可能的寫入在內(nèi)存里進(jìn)行合并,到一定時間或是一定條件后,再一起寫入。其實,在mysql等存儲引擎內(nèi)部,都是有這樣的機制的。打個比方,比如有一個邏輯是修改用戶購買物品的數(shù)量,每次用戶購買物品后,計數(shù)都加一。如果我們現(xiàn)在不是每次都去實時寫磁盤,而是到一定的時間或一定次數(shù)后,再寫入,這樣就可以減少大量的寫入操作。但是,這里需要考慮,如果服務(wù)器宕掉以后,內(nèi)存數(shù)據(jù)的恢復(fù)問題(這一部分會在后面來描述)。因此,如果想簡單的使用數(shù)據(jù)合并,最好是針對數(shù)據(jù)重要性不是很強的業(yè)務(wù),即使丟掉一部分?jǐn)?shù)據(jù),也沒有關(guān)系。
三、多機承擔(dān)請求,分散壓力。如果我們能將原來單機的服務(wù),擴(kuò)充成多機,這樣我們就能很好的將處理能力在一定限度內(nèi)很好的擴(kuò)展。那怎么來做呢?其實有多種方法,我們常用的有數(shù)據(jù)同步和數(shù)據(jù)訂閱。
數(shù)據(jù)同步,我們將所有的更新數(shù)據(jù)發(fā)送到一臺固定的數(shù)據(jù)服務(wù)器上,由數(shù)據(jù)服務(wù)程序處理后,通過日志等方式,同步到其他機器的數(shù)據(jù)服務(wù)程序上。如下圖:
這種結(jié)構(gòu)的好處就是,我們的數(shù)據(jù)基本能保證最終一致性(即:數(shù)據(jù)可能在短暫時間內(nèi)出現(xiàn)不一致,但最后的數(shù)據(jù)能達(dá)到一致),而且結(jié)構(gòu)比較簡單,擴(kuò)展性較好。另外,如果我們需要實時數(shù)據(jù),我們可以通過查詢Master就行。但是,問題也比較明顯,如果負(fù)責(zé)處理和分發(fā)的機器掛掉了,我們就需要考慮單點備份和切換方案。
數(shù)據(jù)訂閱,我們也可以通過這樣的方式來解決數(shù)據(jù)多機更新的問題。這種模式既是在存儲邏輯和數(shù)據(jù)系統(tǒng)前,增加一個叫做Message Queue(消息隊列,簡稱MQ)的東東,前端業(yè)務(wù)邏輯將數(shù)據(jù)直接提交到MQ,MQ將數(shù)據(jù)做排隊等操作,各個存儲系統(tǒng)訂閱自己想要的數(shù)據(jù),然后讓MQ推送或自己拉取需要的數(shù)據(jù)。
MQ不帶任何業(yè)務(wù)處理邏輯,他的作用就是數(shù)據(jù)轉(zhuǎn)發(fā),將數(shù)據(jù)轉(zhuǎn)發(fā)給需要的系統(tǒng)。其他系統(tǒng)拿到數(shù)據(jù)后,自行處理。
這樣的結(jié)構(gòu),好處是擴(kuò)展比較方便,數(shù)據(jù)分發(fā)效率很高。但是問題也比較明顯,因為處理邏輯分散在各個機器,所以數(shù)據(jù)的一致性難以得到保證。另外,因為這種模式看起來就是一個異步提交的模式,如果想得到同步的更新結(jié)果,要做很多附加的工作,成本很高且耦合度很大。還有,需要考慮MQ的單點備份和切換問題。
因為現(xiàn)在數(shù)據(jù)庫(如Mysql)基本帶有數(shù)據(jù)同步功能,因此我們在這個階段比較推薦數(shù)據(jù)同步的方法。至于第二種方式,其實是很好的一種思想,后續(xù)我們會有著重的提及。那再來看我們的架構(gòu),就應(yīng)該演變成這樣的結(jié)構(gòu)。
到目前這個階段,我們基本上就實現(xiàn)了從單機到多機的轉(zhuǎn)變。數(shù)據(jù)的多機化,必然帶來的問題:一致性!這個是否有解決方案?這個時候我們需要引入一個著名的理論:CAP原理。
CAP原理包含了三個要素:一致性(Consistency)、可用性(Availability)、分區(qū)容忍性(Partition tolerance)。三個要素中,最多只能保證兩個要素同時滿足,不能三者兼顧。架構(gòu)設(shè)計時,要根據(jù)業(yè)務(wù)需要進(jìn)行取舍。比如,我們?yōu)榱吮WC可用性和分區(qū)容忍性,可能會舍去一致性。
我們將數(shù)據(jù)分成多機,提高了系統(tǒng)的可用性,因此,一致性的保證很難做到強一致性。有可能做到最終一致性。這也是分布式引入以后的煩惱。
這樣的一個系統(tǒng),也是后續(xù)我們分布式架構(gòu)的一個雛形,雖然比較粗糙,但是他還是比較簡單實用,對于一般中型網(wǎng)站,已經(jīng)能很好的解決問題。
?
【第七階段?:?拆分】
到上面一個階段,我們初步接觸到了邏輯、存儲等的多機模式。這樣的結(jié)構(gòu),對于邏輯不是特別復(fù)雜的網(wǎng)站,足以撐起千萬級的壓力。所以大多數(shù)網(wǎng)站,只要能夠用好上面的結(jié)構(gòu)就可以很好的應(yīng)對服務(wù)壓力了。只不過還有很多細(xì)節(jié)的工作需要精細(xì)化,比如:多機的運維、穩(wěn)定性的監(jiān)控、日志的管理、請求的分析與挖掘等。
如果流量持續(xù)增長,或者是業(yè)務(wù)持續(xù)的擴(kuò)展,上述的架構(gòu)可能又將面臨挑戰(zhàn)。比如,多人開發(fā)常常出現(xiàn)版本沖突;對于數(shù)據(jù)庫的更新量變大;一個表里面的記錄數(shù)已經(jīng)超過千萬甚至過億等等。
怎么解決呢?還記得我們之前介紹過一個CAP理論嘛?三要素里面有一個東東叫:分區(qū)容忍性(Partition tolerance)。其實,這個就是我們接下來解決問題的基礎(chǔ):切分!
一、從數(shù)據(jù)流向來看,切分包括:請求的切分、邏輯的切分、數(shù)據(jù)的切分。
數(shù)據(jù)的切分:將不同的數(shù)據(jù)放到不同的庫中,將原來的單一的一個庫,切分成多個庫。
邏輯的切分:將不同的業(yè)務(wù)邏輯拆分成多份代碼,用不同的代碼管理路徑來管理(如svn目錄)。
請求的切分:將不同的邏輯請求分流到不同的機器上。比如:圖片請求、視頻請求、注冊請求等。
二、從數(shù)據(jù)組織來看,切分包括:水平切分、垂直切分。
數(shù)據(jù)庫的變大通常是朝著兩個方向來進(jìn)行的,一個是功能增加,導(dǎo)致表結(jié)構(gòu)橫向擴(kuò)展;一個是提交數(shù)據(jù)持續(xù)增多,導(dǎo)致數(shù)據(jù)庫表里的數(shù)據(jù)量持續(xù)縱向增加。
數(shù)據(jù)量變大以后,單機性能會下降很明顯,因此我們需要在合適的時候?qū)?shù)據(jù)進(jìn)行切分(這個我沒有太深入的研究過相關(guān)數(shù)據(jù)庫的最合適的切分點,只是從經(jīng)驗上來講,單表的字段數(shù)控制在20個以內(nèi),記錄數(shù)控制在5千萬以內(nèi)會比較好些)。
垂直切分和水平切分,其實是挺糾結(jié)的兩個詞。我之前對這兩個詞經(jīng)常搞混。后來自己畫了個圖,就很直接明了了。
水平切分:
水平切分就是因為記錄數(shù)太多了,需要橫著來一刀,將原來一張表里面的數(shù)據(jù)存入到多張表中,用于減少單張表里的數(shù)據(jù)量。
垂直切分:
垂直切分就是因為業(yè)務(wù)邏輯需要的字段太多,需要豎著來一刀,將原來放在一張表里的所有字段,拆分成多張表,通過某一個Key來做關(guān)聯(lián)(如關(guān)系數(shù)據(jù)庫中的外鍵),從而避免大表的產(chǎn)生。
好了,有了上述的基礎(chǔ)以后,我們再來看實際問題如何來解決。
假設(shè),現(xiàn)在我們有一個博客網(wǎng)站,這個網(wǎng)站擁有多個功能,如:圖片、博客、用戶信息等的插查刪改操作。而現(xiàn)在博客數(shù)據(jù)膨脹比較厲害。
首先,我們從數(shù)據(jù)流向來看,用戶訪問博客、圖片、用戶信息等這幾個邏輯沒有直接的耦合,對應(yīng)的業(yè)務(wù)邏輯關(guān)聯(lián)也很少。
因此,我們第一步從入口上就可以把三者分開。最簡單的方式就是通過域名來切分,比如:img.XXX.com、blog.XXX.com、user.XXX.com。然后通過不同的WebServer來接收這些請求。
第二步,我們的業(yè)務(wù)邏輯代碼,很明顯可以將這些邏輯分開(從部署上分開)。一部分專門處理圖片的請求,如ImageUploadAction/ImageDisplayAction/ImageDeleteAction,一部分專門處理博客請求,如:BlogDisplayAction/BlogDeleteAction,一部分專門處理用戶相關(guān)請求,如:UserModifyAction/UserDisplayAction等等。
第三步,從數(shù)據(jù)庫存儲上,將三者剝離開。簡單的就是分成三個不同的庫。
這樣,從數(shù)據(jù)流向上,我們就按不同的功能,將請求進(jìn)行了拆分。
其次,從數(shù)據(jù)存儲上來看,由于博客數(shù)據(jù)量增長比較快,我們可以將博客的數(shù)據(jù)進(jìn)行水平的拆分。拆分方法很多,比如常用的:
1、按區(qū)間拆分。假定我們用blog_id作為Key,那么我們可以每1千萬,做一次切分。比如[1,1kw)、[1kw,2kw)等等。這樣做的好處就是可以不斷的增長。但訪問可能會因為博客新舊的原因,集中到最新的幾個庫或表中。另外,要根據(jù)數(shù)據(jù)的增長動態(tài)的建表。
2、按取模拆分。比如我們預(yù)估我們的blog_id最多不超過10億,如果每張表里面我們預(yù)估存入1千萬的數(shù)據(jù),那么我們就需要100張表(或庫)。我們就可以按照blog_id % 100?這樣來做切分。這樣做的好處是簡單,表在一開始就全部建立好了。且每個表或者庫的訪問都比較均勻。但問題就是,如果數(shù)據(jù)持續(xù)擴(kuò)張,超出預(yù)期,那么擴(kuò)展性就成為最主要的問題。
3、其他還有一些衍生的方式,比如按Hash值切分等等,大多大同小異。
這樣一來,我們通過訪問模式、數(shù)據(jù)組織等多個維度的拆分以后,我們單機能夠提供服務(wù)的能力就變的比較強悍了。具體的架構(gòu)如下圖。
上述結(jié)構(gòu)看似比較完美,但是在實際的使用中可能會遇到以下幾個問題:
1、業(yè)務(wù)關(guān)聯(lián)問題。多個Service之間不可能沒有任何關(guān)聯(lián),如果出現(xiàn)關(guān)聯(lián),怎么辦?特別是如果是提交的信息要修改多個業(yè)務(wù)的數(shù)據(jù)的時候,這個會比較頭疼。
2、服務(wù)運維問題。這樣拆分以后,隨著機器數(shù)量的膨脹,對于機器的管理將會變的愈發(fā)的困難。這個問題直接會影響到整體架構(gòu)的設(shè)計。面向運維的設(shè)計是架構(gòu)設(shè)計中必須要考慮的重要因素。
3、還有一個問題是我們WebServer始終是單機的,如果出現(xiàn)宕機等問題,那影響將是致命的。這個我們還沒有解決。
這些問題都會在接下來的部分詳細(xì)來解決。
?
【第八階段?:?WebServer多機化】
?????????上面說了這么多,我們的業(yè)務(wù)都基本上運轉(zhuǎn)在只有一個WebServer的條件下。如果出現(xiàn)宕機,所有服務(wù)就停掉了;如果壓力大了,單機不能承載了,怎么辦?
?????????說到這個話題,我們需要來回顧一下在大學(xué)時學(xué)習(xí)的關(guān)于網(wǎng)絡(luò)的基本知識。^_^
?????????拋開復(fù)雜的網(wǎng)絡(luò),我們簡化我們的模型。我們的電腦通過光纖直接連入互聯(lián)網(wǎng)。當(dāng)我們在瀏覽器地址欄里面輸入http://www.XXX.com時,到我們的瀏覽器展現(xiàn)出頁面為止,中間出現(xiàn)了怎么樣的數(shù)據(jù)變化?(注意:為了不那么麻煩,我簡化了很多東西,比如:NAT、CDN、數(shù)據(jù)包切片、TCP超時重傳等等)
上面的圖我們應(yīng)該比較熟悉,同時也應(yīng)該比較清晰的表達(dá)了我們簡化后,從輸入網(wǎng)址到頁面展現(xiàn)的一個過程。中間有兩個東西我們比較關(guān)注,也是解決我們WebServer多機化的關(guān)鍵。
?????????1、DNS服務(wù)是否能幫我們解決多機化?
?????????2、www.XXX.com服務(wù)器的WebServer如何多機化?
?????????首先,如果DNS解析能夠根據(jù)我們的請求來區(qū)分,對于同一域名,將不同的用戶請求,綁定到不同的ip上,這樣,我們就(友情提示:word統(tǒng)計此處已經(jīng)達(dá)到10000字)能部署多個WebServer,對應(yīng)不同的ip,剩下的無非就是多申請幾個ip地址而已。
?????????當(dāng)我們網(wǎng)站比較小的時候,我們都是在代理商處購買域名并由代理商的服務(wù)域名解析服務(wù)器幫我們做域名解析。但是,對于許多大型的網(wǎng)站,都需要對類似于www.XXX.com、blog.XXX.com、img.XXX.com等在XXX.com根下的所有服務(wù)的進(jìn)行域名解析,這樣便于對服務(wù)進(jìn)行控制和管理。而域名的解析往往有專門的策略來處理,比如根據(jù)IP地域、根據(jù)不同請求IP的運營商等返回不同的服務(wù)器IP地址。(大家可能以前也有過這樣的經(jīng)驗:在不同的地方,ping幾個大的網(wǎng)站,看到的ip是不一樣的)。
?DNS策略分析和處理服務(wù)是對請求IP進(jìn)行分析和判斷的系統(tǒng),判斷請求來自哪個地域、哪個運營商,然后根據(jù)內(nèi)部的一些庫的判斷,決定應(yīng)該返回哪個WebServer的IP。這樣,就能盡量保證用戶以最快的速度訪問到對應(yīng)的服務(wù)。
?????????但是,如果我們有大量的WebServer,那每個Server都要有一個IP,另外,我們要增加一個新機器,又要申請一個IP地址,好像很麻煩,且不可接受。怎么辦呢?
?????????第二點,我們需要考慮對于服務(wù)器的WebServer的多機化方式。
?????????我們?yōu)槭裁匆猈ebServer多機化?原因就是因為單機的處理性能不行了,我們要提升處理能力。
?????????那WebServer要做哪些事情?Hold住大量用戶請求連接;根據(jù)URL將請求分流到不同邏輯處理的服務(wù)器上;有可能還有一些防攻擊策略等。其實這些都是消耗CPU的。
?????????如果我們在WebServer前端增加一層,什么邏輯都不處理,就是利用一定的負(fù)載均衡策略將數(shù)據(jù)包轉(zhuǎn)發(fā)給WebServer(比如:工作在IP層,而非TCP層)。那這一層的處理能力跟WebServer比是否是要強悍很多?!這樣的話,這一層后面就可以掛載很多的WebServer,而無需增加外網(wǎng)IP。我們暫且叫這一層叫VS(Virtual Server)。這一層服務(wù)要求穩(wěn)定性較高,且處理邏輯要極為簡單,同時最好工作在網(wǎng)絡(luò)模型中較低的層次上。
?這樣的話,我們就只需要幾個這樣的VS服務(wù)器組,就可以組建大量的WebServer集群。當(dāng)一個群組出現(xiàn)問題,直接可以通過改變IP綁定,就可以切換到其他服務(wù)器組上。
?????????現(xiàn)在這樣的VS實現(xiàn)有多種。有靠硬件方式實現(xiàn)的,也有靠軟件方式實現(xiàn)的。硬件方式實現(xiàn)的話,成本較高,但穩(wěn)定性和效率較好。軟件方式實現(xiàn)的,則成本較低,但穩(wěn)定性和效率較硬件方式要低一些。
?????????現(xiàn)在用的比較多的有開源的LVS(Linux Virtual Server),是由我國的一個博士寫的,NB!以及根據(jù)LVS改寫后的一些變種。
?????????另外還有F5 Networks公司出的收費的F5-BIG-IP-GTM等。(注:這個確實沒用過,以前在網(wǎng)上看過,寫到此處記不清,在百度上搜的。如有錯誤,敬請雅正)。
?????????好了,通過上述的方式,我們基本實現(xiàn)了WebServer的多機化。
?
【第九階段?:?邏輯關(guān)聯(lián)和層次劃分】
?????????在第七階段的時候,我們提到了幾個問題,其中有一個就是業(yè)務(wù)關(guān)聯(lián)問題。當(dāng)我們將業(yè)務(wù)拆分以后,多個業(yè)務(wù)之間沒有了耦合(或者是極弱的耦合),能夠獨立的運轉(zhuǎn)。這個看起來是多么美妙的事情。但是實際情況真是如此嘛?
?????????這樣的業(yè)務(wù)還真是存在的。比如我們有兩個業(yè)務(wù)blog和image。blog可以上傳和展示圖片。那image.XXX.com就提供兩個HTTP服務(wù),一個是上傳的,一個是顯示的。這樣,blog業(yè)務(wù)就可以通過簡單的URL耦合來實現(xiàn)了圖片的這些功能。
?????????但真是所有的情況都是如此的嘛?
?????????我們再看一個例子。比如blog和用戶相關(guān)的業(yè)務(wù)。用戶可以在blog登錄、注銷等,blog需要實時判斷某一個用戶是否登錄等。登錄和注銷兩個操作似乎可以通過類似account.XXX.com提供的login和logout這樣的URL接口實現(xiàn)。但是每次頁面瀏覽要判斷用戶是否已經(jīng)登錄了,出于安全性等多方面的考慮,就不好通過URL來提供這樣的服務(wù)。
?????????那看起來,我們在第七階段提出的按業(yè)務(wù)切分的理想情況,在實施的時候,并不是那樣的完美。在實際的運行中,耦合是不可避免的。
?????????有了耦合,我的第一反應(yīng)基本上就是看看是否能夠借助設(shè)計模式來解決這些的問題。其實呢,設(shè)計模式早已經(jīng)給我們比較好的解決方案(但絕對不是完美的解決方案。俗話說的:沒有最好,只有更好!)。在這篇文章的最初已經(jīng)提到過了,為了增強網(wǎng)站代碼的可重用性,我們引入了一些框架,比如:struts、spring、hibernate等等。其實這些框架,基本上是圍繞著MVC的原則來設(shè)計的。struts、webwork等框架,將視圖和邏輯控制分離;spring負(fù)責(zé)組織業(yè)務(wù)邏輯的數(shù)據(jù);hibernate很好的做了數(shù)據(jù)訪問層的工作,實現(xiàn)了ORM。
?????????那現(xiàn)在我們采用多機分布式的時候,是否可以借鑒這些思想呢?其實也是可以的。
?????????我們來分析一下我們的業(yè)務(wù)。
?????????其實我們的業(yè)務(wù)大多可以分為兩類:
?????????一、與實際的產(chǎn)品相關(guān)的業(yè)務(wù),比如:blog、news等等。這些業(yè)務(wù)之間的耦合度不是很高,往往可以通過提供HTTP的接口即可實現(xiàn)業(yè)務(wù)需要的互通。因此,從這個層面上來看,是可以基本做到業(yè)務(wù)垂直拆分的。
?????????二、基礎(chǔ)服務(wù),比如:用戶帳號管理、消息通知等等。這些服務(wù)往往被多個業(yè)務(wù)所依賴。他們需要提供更通用的、更安全的、更穩(wěn)定的接口和服務(wù)。但是,關(guān)于基礎(chǔ)業(yè)務(wù)的理解和劃分,是沒有一個特定的規(guī)則的。比如,image圖片服務(wù),他有可能剛開始是一個業(yè)務(wù)服務(wù),到一定階段以后,多個系統(tǒng)需要對他有強的依賴,自然也就成為了一個基礎(chǔ)服務(wù)。
?????????所以,從上面的描述,我們可以發(fā)現(xiàn)服務(wù)的類型,并不是固定的。要很好的解決服務(wù)耦合的問題,也并沒有一個十分完美的解決方案。我們能做的就是盡量降低耦合,通過某種方式,能夠很好的達(dá)到耦合和可維護(hù)性的平衡。
?????????總的來看,MVC模式其實給我們提供了一個比較好的方案。
?????????我們把系統(tǒng)從兩個維度上進(jìn)行劃分:垂直(業(yè)務(wù))和水平(邏輯)。
?我們做了這樣的劃分以后,似乎看起來沒有實質(zhì)性的改變。但是,我們可以明確了我們設(shè)計的原則,并強化了代碼的可復(fù)用性。另外,最關(guān)鍵的是,服務(wù)之間的依賴和耦合關(guān)系,有了明確的地方來做。同時,我們還可以將業(yè)務(wù)內(nèi)部的結(jié)構(gòu)進(jìn)行拆分,更好的增強復(fù)用。
?????????數(shù)據(jù)訪問和組織層、數(shù)據(jù)存儲層,這兩個位于下游的層次,應(yīng)該是屬于系統(tǒng)內(nèi)部的層次,原則上是最好不要對外開放接口的,否則,系統(tǒng)間的耦合就會非常的大。并且可維護(hù)性會非常的難。而邏輯控制和視圖層,實際上是提供對外(對用戶或者是外系統(tǒng))最好的訪問的入口。當(dāng)然,這個入口可以是HTTP協(xié)議的,也可以是非HTTP協(xié)議的。
?????????比如,對于account服務(wù),可以提供基于HTTPS對用戶開放的login和logout服務(wù),也提供基于XXX Protocol數(shù)據(jù)交換的協(xié)議的給內(nèi)部的get_session服務(wù)。從簡單的設(shè)計上來看,只是根據(jù)服務(wù)不同,提供不同的數(shù)據(jù)交換格式、以及不同的安全控制。這樣也是秉承了一個高內(nèi)聚低耦合的原則。
?????????這里還有幾個及其重要的問題沒有詳細(xì)的提及:系統(tǒng)內(nèi)外的數(shù)據(jù)傳輸協(xié)議、接口API、服務(wù)訪問定位。這幾個問題實際上還跟運維問題緊密相關(guān),都會放到后面來詳細(xì)討論。
?
【第十階段?:?數(shù)據(jù)存儲優(yōu)化】
?????????在前面的階段中,我們都使用數(shù)據(jù)庫作為默認(rèn)的存儲引擎,很少談?wù)撽P(guān)于關(guān)于數(shù)據(jù)存儲的話題。但是,數(shù)據(jù)的存儲卻是我們現(xiàn)在眾多大型網(wǎng)站面臨的最核心的問題。現(xiàn)在眾多網(wǎng)絡(luò)巨頭紛紛推出自己的“高端”存儲引擎,也吸引了眾多的眼球。比如:google的BigTable、facebook的cassandra、以及開源的Hadoop等等。國內(nèi)眾多IT巨頭也紛紛推出自己的“云”存儲引擎。
?????????其實這些存儲引擎用的一些關(guān)鍵技術(shù)有許多的共性,比如:Meta信息管理、分片、冗余備份、數(shù)據(jù)自動恢復(fù)等。因為之前我也做過一些工作和研究,但是不是特別深入,不敢在此指手畫腳、高談闊論。相關(guān)的資料網(wǎng)上比比皆是,大家有興趣有search吧。^_^
?????????關(guān)系型數(shù)據(jù)庫常用的有幾個,比如:MySQL、PostgreSQL、SQLServer、DB2等,當(dāng)然還有NB的Oracle(唉,作為DB科班出生的屌絲,沒有真正意義上使用過這樣的高帥富數(shù)據(jù)庫,慚愧啊)。
?????????互聯(lián)網(wǎng)使用頻率最高的應(yīng)該要算是MySQL。最重要的是開源;其次是他提供的一些特性,比如:多種存儲引擎、主從同步機制等,使用起來非常的方便;再次,就是一個單詞:LAMP,幾乎成為搭建網(wǎng)站的必備利器;還有,較高服務(wù)的穩(wěn)定性。
?????????關(guān)系型數(shù)據(jù)使得建立網(wǎng)站變得及其輕松,幾乎是個網(wǎng)站都會有一個數(shù)據(jù)庫。試想一下,如果沒有這種通用的關(guān)系型數(shù)據(jù)庫,我們的生活會是怎么樣的?
?????????關(guān)系型數(shù)據(jù)庫在95%的場景下是工作的非常好的。而且只要配置得當(dāng)、數(shù)據(jù)切分合理、架構(gòu)設(shè)計符合要求,性能上是絕對能夠承受業(yè)務(wù)的需求。現(xiàn)在很多大型網(wǎng)站的后臺,幾乎都是數(shù)據(jù)庫作為標(biāo)準(zhǔn)的存儲引擎。
?????????另外,最近炒的比較熱的一個概念就是NoSQL。說起來,其實就是放棄關(guān)系型數(shù)據(jù)庫中許多的特性,比如:事務(wù)、外鍵等等。簡化設(shè)計,將視線更關(guān)注于存儲本身。比較有名的,比如:BDB、MongoDB、Redis等等。這些存儲引擎提供更為直接的Key-Value存儲,以滿足互聯(lián)網(wǎng)高效快速的業(yè)務(wù)需求。其實,從另外一個角度來看,關(guān)系型數(shù)據(jù)庫(比如MySQL),如果不使用那么多的關(guān)系型數(shù)據(jù)庫特性,也可以簡化成KV模式,提升效率。
?????????不過,有些時候,為了節(jié)省機器資源,提升存儲引擎的效率,就不得不開發(fā)針對業(yè)務(wù)需要的專用存儲引擎。這些存儲引擎的效率,往往較關(guān)系數(shù)據(jù)庫效率高10-100倍。比如,當(dāng)一個圖片服務(wù),存儲的圖片量從1億到10億,甚至100億;現(xiàn)在流行的微薄,假如發(fā)布總量達(dá)到10億或者100億。這樣級別的數(shù)據(jù)量,如果用數(shù)據(jù)庫來存儲固然可以,但是有可能需要耗費相當(dāng)多的機器,且維護(hù)成本和代價不小。
?????????其實分析我們通常的業(yè)務(wù),我們對數(shù)據(jù)的操作無非就是四個:查、插、刪、改。對應(yīng)數(shù)據(jù)庫的操作就是select、insert、delete、update。那自己設(shè)計的存儲引擎無非就是對這四個操作中的某幾個做針對性的優(yōu)化,讓其中幾個根據(jù)不同的業(yè)務(wù)特點,使其變的更加的高效。比如:對于微薄而言,可能就需要插入和查詢具有很高的效率,而刪除和修改的需求不是那么高。這個時候,就可以犧牲一部分刪除和修改的性能,而重點放在插入和查詢上。
?????????在業(yè)務(wù)上,我們的提交通常可以看作在某一個維度上是有序的。比如,每一個微薄或者博客可能都會有一個id,這些id可能是按照序列遞增、或是時間遞增等。而查詢的時候,則是按照另外一個維度的順序組織的,比如:按關(guān)注的人組織微薄的信息。這樣就造成了一個沖突,就是提交的組織順序和查詢的組織順序不一致。
?????????再來看看我們的磁盤。我們現(xiàn)在常規(guī)磁盤還是機械方式運轉(zhuǎn)的:有盤片、有磁頭等等。當(dāng)需要寫入或者讀取的時候,磁頭定位到不同的盤片的不同扇區(qū)上,然后找到或修改對應(yīng)的信息。如果信息分散在不同的盤片、扇區(qū)上,那么磁頭尋道的時間就會比較長。
?????????反過來,我們再來看看我們的業(yè)務(wù)和磁盤的組織。A、如果我們按照寫入有序的方式存儲數(shù)據(jù),那么磁盤會以很高的效率,將數(shù)據(jù)連續(xù)的寫入到盤片中,無需多次尋道。那么讀取的時候,可能就會出現(xiàn)按照另外的維度來組織數(shù)據(jù),這樣就有可能需要在多個地方來讀取。從而造成我們磁盤來回尋道定位,使得查詢效率低下。B、如果我們按照某一種查詢維度來存儲數(shù)據(jù)(因為同一業(yè)務(wù)往往有多種查詢模式),那讀取的時候,就讓磁盤順序讀取即可。但帶來的麻煩就是,寫入的時候有可能需要反復(fù)的尋道定位,將提交的數(shù)據(jù)一條條寫入。這樣就會給寫入帶來麻煩。
?????????其實矛盾的主體就是:僵化的磁盤存儲方式不能滿足網(wǎng)站日益增長的提交和查詢需求!
?????????是否有解決方案呢?那必須有!
?????????要解決這個問題,可以從兩方面入手。
?????????1、改變現(xiàn)有的磁盤存儲方式。隨著硬件快速的發(fā)展,磁盤本身的效率得到了極大提升。磁盤的轉(zhuǎn)速,盤片的個數(shù)等都大幅增加,本身尋道的速度提升很快。加上緩存等的加強,效率提升還是很明顯。另外,Flash Disk、Solid State Disk等新技術(shù)的引入,改變了原來的隨機讀取的低效(沒有數(shù)據(jù),根據(jù)經(jīng)驗,Flash或SSD的效率可以達(dá)到10-100倍普通硬盤的隨機訪問的效率)。
?????????2、根據(jù)不同的業(yè)務(wù),有效的組織數(shù)據(jù)。比如微薄(我沒有寫過微薄,但是做過微薄類似的東東),因為讀取業(yè)務(wù)組織的維度是按人,而提交組織的維度則是時間。所以,我們可以將某個人一段時間提交的數(shù)據(jù),在內(nèi)存里面進(jìn)行合并,然后再一次性的刷入到磁盤之中。這樣,某個人一段時間發(fā)布的數(shù)據(jù)就在磁盤上連續(xù)存儲。當(dāng)讀取的時候,原來需要多次讀取的數(shù)據(jù),現(xiàn)在可以一次性的讀取出來。
?????????第一點提到的東東現(xiàn)在也逐步的開始普及,其實他給我們的改變是比較大的。不用花太多的精力和時間,去精心設(shè)計和優(yōu)化一個系統(tǒng),而只需要花一些錢就能使得性能大幅提升,而且這樣的成本還在降低。
?????????但是,資源永遠(yuǎn)是不夠的。多年前,當(dāng)內(nèi)存還是64K的時候,我們暢想如果內(nèi)存有個32M該多美妙啊。但是,隨著數(shù)據(jù)的膨脹,即使現(xiàn)在64G內(nèi)存,也很快就不能滿足我們的需求。
?????????所以,在一些特殊的應(yīng)用下面,我們還是需要更多的關(guān)注第二個點。
?????????對于存儲優(yōu)化,一直是一個持久的話題,也有很多成熟的方案。我這里可能提幾個點。
?
【階段性小結(jié)】
?????????經(jīng)過了上述的架構(gòu)擴(kuò)展和優(yōu)化以后,我們的系統(tǒng)無論是從前端接入,還是后端存儲都較最初的階段有了質(zhì)的變化。這樣的架構(gòu)足以支撐起10億級別的流量和10億級別的數(shù)據(jù)量。我們具體的來看一下整體的架構(gòu)。
??上述的模型是我個人覺得的一個比較理想的模型。Virtual Server Cluster接收數(shù)據(jù)包,轉(zhuǎn)發(fā)給Web Server Cluster或者Private Protocol Server Cluster(如果有的話)。然后視圖和邏輯層server負(fù)責(zé)調(diào)用cache或者數(shù)據(jù)訪問組織層的接口,返回處理后的數(shù)據(jù)。定制存儲系統(tǒng)、通用存儲系統(tǒng)和數(shù)據(jù)庫集群,提供基本的數(shù)據(jù)。
?????????每一個層次通過負(fù)載均衡和一定的協(xié)議來獲取下一層提供的數(shù)據(jù),或者提交數(shù)據(jù)。在存儲系統(tǒng)內(nèi)部,通過Meta信息管理、主從同步、消息訂閱等方式,實現(xiàn)數(shù)據(jù)的同步。
?????????如果我們再要擴(kuò)大規(guī)模,比如:機器數(shù)擴(kuò)展到上千臺、萬臺。對于我們來說,管理機器就成為了機器頭等的大問題。
?????????同時,我們之前還有幾個問題沒有很詳盡的描述,比如:數(shù)據(jù)傳輸協(xié)議、遠(yuǎn)程系統(tǒng)調(diào)用、系統(tǒng)的異構(gòu)性等等,這些都是會影響到我們系統(tǒng)可維護(hù)性的大問題。
?????????我7年前就開始使用Java,到現(xiàn)在,總算能看懂一些東西了。J2EE我個人覺得確實是一個比較偉大的東東。里面其實早已經(jīng)提出了一套比較完善的解決大型或者超大型網(wǎng)站的整體解決方案。
?????????比如:
?????????1、JNDI(Java Naming and Directory Interface):描述了如何使用命名和目錄等的規(guī)范,使得我們能夠?qū)⒎?wù)作為資源掛接到命名服務(wù)器上,并且通過標(biāo)準(zhǔn)的接口進(jìn)行訪問。這對我們管理巨大的機器資源和服務(wù)提供了很好的方案。
?????????2、RMI(Remote Method Invoke):遠(yuǎn)程過程調(diào)用。即,通過標(biāo)準(zhǔn)的RMI接口,可以輕松實現(xiàn)跨系統(tǒng)的遠(yuǎn)程調(diào)用。
?????????3、IDL(Interface Definition Language):接口定義語言。這個本來是CORBA中用來訪問異構(gòu)系統(tǒng)對象的統(tǒng)一語言,其實也給我們提供了跨系統(tǒng)調(diào)用中,對外接口的定義方案。
?????????4、JDBC(Java Database Connectivity):數(shù)據(jù)庫訪問接口。屏蔽了不同數(shù)據(jù)庫訪問的實現(xiàn)細(xì)節(jié),使得數(shù)據(jù)庫開發(fā)變得輕松。
?????????5、JSP(Java Server Page):實現(xiàn)將視圖和控制層很好分離的方式。
?????????6、JMS(Java Message Service):用于和面向消息的中間件相互通信的應(yīng)用程序接口。提供了很好的消息推送和訂閱的機制。
?????????以上這些組件其實很好的協(xié)助構(gòu)建了J2EE整體架構(gòu)。我的很多想法都來源于這些東東。后續(xù)會結(jié)合這些,詳細(xì)來分析諸如資源命名位置服務(wù)、數(shù)據(jù)傳輸協(xié)議、異構(gòu)系統(tǒng)接口定義等解決大規(guī)模機器運維問題的方案。
?
【第十一階段?:命名位置服務(wù)】
?????????在前面我們不止一次提到了命名位置服務(wù)(Naming & Location Service)。在不同的架構(gòu)或者公司里面,這個名字往往不一樣,比如,在java里面叫JNDI(Java Naming & Directory Interface),在有些地方可能會叫做資源位置系統(tǒng)(Resource Location System)。
?????????總之,不管叫什么名字,我們要知道的就是為什么要有這樣的系統(tǒng)?他能做哪些事情?他有哪些實現(xiàn)方式?等等。
?????????在我們之前的章節(jié)中,我們的服務(wù)從一臺單機擴(kuò)展到十臺左右的多機,到成百上千臺機器。我們的服務(wù)從單一的一個服務(wù)擴(kuò)展到成百上千的服務(wù)。這么多的機器、服務(wù),如果不好好管理,我們就崩潰了。比如,我們的服務(wù)A要連接服務(wù)B,如果現(xiàn)在采用配置IP的方式,可能需要配置幾十臺機器的IP,如果其中某些機器出現(xiàn)了變更,那所有服務(wù)A連接服務(wù)B的IP都要改變。如果所有的服務(wù)都是這樣,這將是多么痛苦的一件事情?Orz。
?????????如果我們只是簡單的將我們的服務(wù)看成是一個個的資源(Resource),這些資源可以是數(shù)據(jù)庫,可以是cache,可以是我們自己寫的服務(wù)。他們都有一個共同的特點,就是在某一個IP上,打開一個PORT,遵循一定的協(xié)議,提供服務(wù)。
?????????我們先簡單的來構(gòu)建這樣一個模型。我們這里先抽象一個接口,叫做Interface Resource。這個接口下面有多個實現(xiàn),比如:DBResource、CacheResource、ImageResource等等。具體類圖如下:
有了這樣的一個層次結(jié)構(gòu)以后,我們?yōu)榱说玫侥骋粋€實例,有多種方式,比如:
?????????1、直接生成的方式:
???????????????????Resource r = new ImageResource();
?????????2、間接生成的方式:
???????????????????A、比如我們在設(shè)計模式中經(jīng)常使用到的工廠模式:
???????????????????Resource r = ResourceFactory.get(“Image”);
???????????????????B、IoC(Inversion of Control)方式:
???????????????????Resource r = (Resource)Container.getInstance(“ImageResource”);
?????????我們打一個不是很完全匹配的比方。我們?nèi)绻苯釉诜?wù)中采用IP配置的方式,就類似于直接生成實例一樣,如果實例發(fā)生變化,或者要調(diào)整生成的對象,就需要修改調(diào)用者的代碼。也就是說,如果其他服務(wù)的IP發(fā)生變化,我們調(diào)用者就需要修改配置,重啟程序等等。而且對于如果有很多很多這樣的服務(wù),我們就崩潰了。
?????????那么,我們覺得更好的一種方式呢,就是,如果有一個工廠,或者一個容器,來幫我們管理這一堆的服務(wù)IP、端口、協(xié)議等等,我們要用的時候,就只需要叫一聲:“給我XXX服務(wù)的實例”,那是多么美妙的事情啊!
?????????其實呢,我們的命名位置服務(wù)要做的就是這樣的事情。他類似于一個Meta Server,記錄所有服務(wù)的IP、port、protocol等基礎(chǔ)信息,以及檢查這些服務(wù)的健康狀態(tài),提供給調(diào)用者最基礎(chǔ)的信息服務(wù)。同時,再結(jié)合調(diào)用時的負(fù)載均衡策略,就可以幫我們提供很好的資源管理方式。
?????????這個服務(wù),提供注冊、注銷、獲取列表等接口。他的存在,就將直接關(guān)聯(lián)的兩個服務(wù)給很好的解耦了。我們看看對比:
在沒有Naming Location Service的時候,我們的服務(wù)相互直接依賴,到最后,關(guān)聯(lián)關(guān)系及其復(fù)雜,可能完全沒有辦法維護(hù)。
?????????如果我們增加Naming Location Service以后,這個狀態(tài)就可以得到極大的改善。
?這個時候,我們所有的服務(wù)都在NLS上注冊,同時向NLS獲取其他服務(wù)的信息,所有的信息都匯聚到NLS上管理。
?????????有了這個服務(wù),就好類比成我們生成一個類的時候,采用間接的方式生成。
?????????Service s = (Service) NamingService.getService(“Image”);
?????????好,有了這樣一個架構(gòu)以后,我們可能會關(guān)注這個NLS如何來實現(xiàn)。
?????????實現(xiàn)這個服務(wù)有簡單的方式,也有復(fù)雜的方式。關(guān)鍵是要考慮以下幾個方面:
?????????1、如何找到這個NLS。NLS是所有服務(wù)的入口,他應(yīng)該是有一個不變的地址來保證我們的服務(wù)。因此,我們可以使用我們之前提到過的Virtual Server的方案,通過一個(或多個)固定的域名或者IP來綁定這個服務(wù)。
?????????2、可用性(Availability)和數(shù)據(jù)一致性(Consistency)。因為這個服務(wù)是一個最基礎(chǔ)的服務(wù),如果這個服務(wù)掛掉了,其他服務(wù)就沒有辦法來定位了。那么這個服務(wù)的穩(wěn)定和可靠性就是及其重要的。解決方案有如下幾種:
?????????A、單機實現(xiàn)服務(wù),本地增加備份。我們用單機來實現(xiàn)這樣一個服務(wù),這樣可以保證絕對的數(shù)據(jù)一致性。同時,每次請求數(shù)據(jù)后,每個服務(wù)本地保留一份備份數(shù)據(jù)。當(dāng)這個服務(wù)掛掉了,就使用最近的一次備份。這個方案對于大多數(shù)情況是足以應(yīng)付的,而且具備簡單粗暴有效的特點。
?????????B、多機服務(wù),數(shù)據(jù)同步。采用多機提供服務(wù),信息更新時進(jìn)行數(shù)據(jù)同步。這種方式的優(yōu)點就是服務(wù)可以保證7*24小時服務(wù),服務(wù)穩(wěn)定性高。但是,問題就是維護(hù)成敗會比上面一種方式高。
?????????3、功能。實現(xiàn)服務(wù)名稱到IP、Port、Protocol等信息的一個對應(yīng)。如果要設(shè)計的更通用,比如可以注冊任何信息,就只需要實現(xiàn)Key-Value的通用數(shù)據(jù)格式。其中,Value部分需要支持更多更豐富的結(jié)構(gòu),比如List、Set等。
?????????有了這樣的一個系統(tǒng),我們就可以很方便的擴(kuò)展我們的服務(wù),并且能很好的規(guī)范我們服務(wù)的獲取、訪問接口。
?
【第十二階段?:傳輸協(xié)議、接口、遠(yuǎn)程調(diào)用】
?????????這一部分主要談?wù)勱P(guān)于協(xié)議、接口和遠(yuǎn)程調(diào)用相關(guān)的內(nèi)容。本來這一部分應(yīng)該在之前就有比較詳細(xì)的討論,不過我放到后面來,足見其重要性。特別是在系統(tǒng)越來越多的時候,這幾個東東直接決定了我們的開發(fā)速度和運維成本。
?????????好,接下來我們一個個的看。
?????????1、傳輸協(xié)議
?????????到目前為止,在不同系統(tǒng)之間獲取數(shù)據(jù)的時候,你是采用那種方式呢?
?????????我們簡單看一個例子:
?以上這個可能是我們最(|兩萬字的分隔線|)初學(xué)習(xí)網(wǎng)絡(luò)編程的時候,最常使用的一種C-S交互方式。其實這里面我們已經(jīng)定義了一種交互協(xié)議,只是這種方式顯得比較山寨,沒有規(guī)范。擴(kuò)充性等等都沒有充分考慮。
?????????我們在學(xué)習(xí)網(wǎng)絡(luò)編程的時候,老師就給我們講過,網(wǎng)絡(luò)分層的概念,經(jīng)典的有5層和7層模型。在每一層里面,都有自己的協(xié)議。比如:IP協(xié)議、TCP協(xié)議、HTTP協(xié)議等等。這些協(xié)議基本上都由兩部分組成:頭+數(shù)據(jù)。
?????????【頭信息】
?????????我們來看看TCP協(xié)議:
(注:以上是我從百度百科上截取的)
?????????頭信息中,一般可能會包含幾個重要的元素:協(xié)議標(biāo)識符、版本號、串號(或是本次交互的id)、數(shù)據(jù)包長度、數(shù)據(jù)校驗等信息,可能有些協(xié)議還會帶一些其他數(shù)據(jù),比如數(shù)據(jù)發(fā)出方名稱、接收方名稱、時間、保留字段等等信息。
?????????這些信息的目的,就是為了清晰的表達(dá),我是怎么樣的一個協(xié)議,我有哪些特征,我?guī)У臄?shù)據(jù)有多大。方便接收方能夠清晰的辨認(rèn)出來。
?????????【數(shù)據(jù)部分】
?????????數(shù)據(jù)部分是為了讓應(yīng)用層更好的通訊和表達(dá)數(shù)據(jù)。要達(dá)到的目標(biāo)就是簡潔高效、清晰明了。說起來很容易,但是實現(xiàn)起來要考慮的東西就比較多,比如:數(shù)據(jù)壓縮、字符轉(zhuǎn)移、二進(jìn)制數(shù)據(jù)表達(dá)等等。
?????????我們通常有多種格式來作為數(shù)據(jù)部分的協(xié)議。大體上可以分為:
?????????A、二進(jìn)制流。比如:C里面的結(jié)構(gòu)體、JAVA里面的Serializable,以及像Google的protobuf等。將內(nèi)存里面實體的數(shù)據(jù),按字節(jié)序列化到緩沖區(qū)。這種方式的好處就是數(shù)據(jù)非常緊湊,幾乎沒有什么浪費。但是,問題也比較明顯。雙方必須很清楚協(xié)議,面向的語言基本上是要求一樣的,很難做兼容,跨平臺差。且擴(kuò)展性比較差。另外,還有網(wǎng)絡(luò)大小端字節(jié)序(Big-Endian、Little-Endian)的問題需要考慮。
?????????B、文本傳輸協(xié)議。就是以字符串的方式來組織信息。常見的有XML、JSON等等。這種方式的好處就是擴(kuò)展性強,跨平臺兼容能力好,接口標(biāo)準(zhǔn)且規(guī)范。問題就是傳輸量比較大,需要考慮做壓縮或者優(yōu)化。
?????????XML方式:
?JSON方式:
?因此,我們可以按照我們實際的需求,來定制我們想要的數(shù)據(jù)格式,從而達(dá)到高效和易于表達(dá)和擴(kuò)展的效果。
?????????經(jīng)過上述分析,我們基本上對傳輸協(xié)議有了一個比較大致的了解。有了傳輸協(xié)議之后,我們的跨機器間的數(shù)據(jù)交互,才顯得比較規(guī)范和具有擴(kuò)展性。如果我們還不想自己來定義協(xié)議,我們可以用現(xiàn)有的協(xié)議進(jìn)行組合,比如:HTTP+XML、HTTPS+JSON等等。只要在一個平臺上,大家都遵守這樣的規(guī)范,后續(xù)開發(fā)起來就變得輕松容易。
?????????2、接口(或者API)
?????????接口是我們的服務(wù)對外表達(dá)的窗口。接口的好壞直接決定了我們服務(wù)的可表達(dá)性。因此,接口是一個承上啟下的作用。對外,很好的表達(dá)提供的服務(wù)名稱、參數(shù)、功能、返回的數(shù)據(jù)等;對內(nèi),能夠自動生成描述所對應(yīng)的代碼函數(shù)框架,讓開發(fā)者編寫實現(xiàn)。
?????????我們描述我們接口的方式有很多,可以利用描述性語言來表達(dá)。比如:XML、JAVA里提供的IDL(Interface Definition Language)等等。我們把這種描述接口的語言統(tǒng)一稱為IDL(Interface Definition Language)。
我們可以自己開發(fā)一些工具,將IDL進(jìn)行翻譯,轉(zhuǎn)換成方便閱讀的HTML格式、DOC、CHM等等。方便其他開發(fā)者查閱。
?????????同時,另外一方面,我們可以將IDL轉(zhuǎn)換成我們的接口代碼,讓服務(wù)接口的開發(fā)者和調(diào)用方的開發(fā)者按規(guī)范和標(biāo)準(zhǔn)來實現(xiàn)。
?
?對于下層代碼如何來實現(xiàn)數(shù)據(jù)的解析、函數(shù)的調(diào)用、參數(shù)的傳遞、數(shù)據(jù)的轉(zhuǎn)換和壓縮、數(shù)據(jù)的交換等等工作,則由工具來生成。對上完全屏蔽。詳細(xì)的內(nèi)容,我們將在接下來的遠(yuǎn)程調(diào)用中來分析。
?????????3、遠(yuǎn)程調(diào)用
?????????我們最初寫代碼的時候,就被教授了函數(shù)的概念。我們可以將一些公用的代碼,或者實現(xiàn)一定含義或邏輯的代碼,做成一個函數(shù),方便重復(fù)的使用。
?????????最開始,這些函數(shù)往往在同一個文件里,我們只要先申明,即可使用。
?????????后來,我們開始使用庫里面的函數(shù),或是將函數(shù)封裝成一個個的庫(比如C里面的靜態(tài)庫.a或者動態(tài)庫.so,或者是Java里面的.jar)。
?????????以上對于函數(shù)的調(diào)用,以及函數(shù)自身的處理都是在本地。假如,當(dāng)我們單機不能滿足需求的時候,我們就需要將函數(shù)的處理放到其他機器上,讓機器做到并行的計算。這個時候,我們就需要遠(yuǎn)程的函數(shù)調(diào)用,或者叫做遠(yuǎn)程過程調(diào)用(Remote Procedure Call?或者?Remote Method Invoke,簡稱RPC或者RMI)。
?????????我們在這之前講過幾個東東:負(fù)載均衡、命名位置服務(wù)、協(xié)議、接口。其實前面講這幾個東東都是為了給遠(yuǎn)程過程調(diào)用做鋪墊。RPC都是建立在以上部分的基礎(chǔ)上。
?????????還是按照我們之前分析問題的思路:為什么要這個東東?這個東西解決什么問題?如何實現(xiàn)?有哪些問題?等等來分析RPC吧。
?????????RPC的目的,就是使得從不同服務(wù)上獲取數(shù)據(jù)如同本地調(diào)用一樣方便和自然。讓程序調(diào)用者不需要了解網(wǎng)絡(luò)細(xì)節(jié),不用了解協(xié)議細(xì)節(jié),不用了解服務(wù)的機器狀態(tài)細(xì)節(jié)等等。如果沒有RPC,其實也是可以的,就是我們寫程序的時候難受點而已,哈哈。
?????????接下來,我們看看如何來實現(xiàn)。
以上是整個的一個大體靜態(tài)邏輯。最先編寫調(diào)用的IDL,完成后由工具生成接口說明文檔(doc);同時,生成客戶端調(diào)用代碼(stub,我們叫做存根);另外,需要生成server端接口框架(skeleton),接口開發(fā)者實現(xiàn)具體的代碼邏輯。?
以上就是客戶端調(diào)用的整個邏輯。
?
【第十三階段?:分布式計算和存儲的運維設(shè)計與考慮】
?????????以上的部分已經(jīng)從前到后的將系統(tǒng)架構(gòu)進(jìn)行了描述,同時針對我們會遇到的問題進(jìn)行了分析和處理,提出了一些解決方案,以保證我們的系統(tǒng)在不斷增長的壓力之下,如何的良好運轉(zhuǎn)。
?????????不過,我們很少描述運維相關(guān)的工作,以及設(shè)計如何和運維相關(guān)聯(lián)。系統(tǒng)運維的成敗,直接決定了系統(tǒng)設(shè)計的成敗。所以系統(tǒng)的運維問題,是設(shè)計中必須考慮的問題。特別是當(dāng)我們有成千上萬的(tens of thousands)臺機器的時候,運維越發(fā)顯得重要。因此,在系統(tǒng)設(shè)計初期,就應(yīng)當(dāng)把運維問題納入其中來進(jìn)行綜合的考慮。
?????????如果我們用人來管機器,在幾臺、幾十臺機器的時候是比較可行的。出了問題,人直接上,搞定!不過,當(dāng)我們有幾百臺、幾千臺、幾萬臺、幾十萬臺機器的時候,我們?nèi)绻屓巳ジ愣?#xff0c;那就未見得可行了。
?????????首先,人是不一定靠譜的。即使再聰明可靠的人,也有犯錯誤的時候。按照一定概率計算,如果我們機器數(shù)量變多,那么出錯的絕對數(shù)量也是很大的。同時,人和人之間的協(xié)作也可能會出現(xiàn)問題。另外,每個人的素質(zhì)也是不一樣的。
?????????其次,隨著機器數(shù)量的膨脹,需要投入更多的人力來管理機器。人的精力是有限的,機器增多以后,需要增加人力來管理機器。這樣的膨脹是難以承受的。
?????????再次,人工恢復(fù)速度慢。如果出現(xiàn)了故障,人工來恢復(fù)的速度是比較慢的,一般至少是分鐘級別。這對于要提供7×24小時的服務(wù)來說,系統(tǒng)穩(wěn)定運行的指標(biāo)是存在問題的。同時,隨著機器的增多,機器出問題的概率一定的條件下,絕對數(shù)量會變多,這也導(dǎo)致我們的服務(wù)會經(jīng)常處于出錯的情況之下。
?????????還有,如果涉及到多地機房,如何來管理還是一個比較麻煩的事情。
?????????如果我們能轉(zhuǎn)換思維,在設(shè)計系統(tǒng)的時候,如果能有一套自動化管理的模式,借助電腦的計算能力和運算速度,讓機器來管理機器,那我們的工作就輕松了。
?????????比如,我們可以設(shè)計一套系統(tǒng),集成了健康檢查、負(fù)載均衡、任務(wù)調(diào)度、自動數(shù)據(jù)切片、自動數(shù)據(jù)恢復(fù)等等功能,讓這套系統(tǒng)來管理我們的程序,一旦出現(xiàn)問題,系統(tǒng)可以自動的發(fā)現(xiàn)有問題的機器,并自動修復(fù)或處理。
?????????當(dāng)然以上都是比較理想的情況。凡事沒有絕對之說,只是需要盡可能達(dá)到一個平衡。
?????????好了,說了這么多的問題,要表述的一點就是:人來管理機器是不靠譜的,我們需要盡量用機器來管理機器!
?????????接下來,我們比較簡單的描述一下一個比較理想的自動化管理模型。
?????????我們將我們的系統(tǒng)層次進(jìn)行初步的劃分。
我們將系統(tǒng)粗略的劃分為三個層次:訪問接入層、邏輯處理層和數(shù)據(jù)存儲層。上一次對下一層進(jìn)行調(diào)用,獲取數(shù)據(jù),并返回。
?????????因此,如果我們能夠做到,說下一層提供給上一層足夠可靠的服務(wù),我們就可以簡化我們的設(shè)計模型了。
?????????好,那如果要提供足夠可靠的服務(wù),方便調(diào)用的話,應(yīng)該如何來做呢?
?????????我們可以針對每一層來看。
?????????首先,看看訪問接入層:
?所有的Web Server和?Private Protocol Server將服務(wù)注冊到命名和定位服務(wù)上。一旦注冊后,NLS會定期去檢查服務(wù)的存活,如果服務(wù)宕掉,會自動摘除,并發(fā)出報警信息,供運維人員查閱。等服務(wù)恢復(fù)后,再自動注冊。
?????????Virtual Server從NLS獲取服務(wù)信息,并利用負(fù)載均衡策略去訪問對應(yīng)的后繼服務(wù)。而對外,只看到有一臺Virtual Server。
?????????接下來,我們看看邏輯層:
?由于HTTP的無狀態(tài)性,我們將邏輯代碼按標(biāo)準(zhǔn)接口寫成一個個的邏輯處理單元,放入到我們的邏輯處理容器之中,進(jìn)行統(tǒng)一的運行。并通過容器,到NLS中注冊。Virtual Server通過NLS獲取到對應(yīng)的信息,并通過負(fù)載均衡策略將數(shù)據(jù)轉(zhuǎn)發(fā)到下游。
?????????這里比較關(guān)鍵的數(shù)據(jù)處理單元,實際就是我們要寫的業(yè)務(wù)邏輯。業(yè)務(wù)邏輯的編寫,我們需要嚴(yán)格按照容器提供的規(guī)范(如IDL的標(biāo)準(zhǔn)等),并從容器獲取資源(如存儲服務(wù)、日志服務(wù)等)。這樣上線也變的簡單,只需要將我們的處理單元發(fā)布到對應(yīng)的容器目錄下,容器就可以自動的加載服務(wù),并在NLS上注冊。如果某一個服務(wù)出現(xiàn)異常,就從NLS上將其摘掉。
?????????容器做的工作就相對比較多。需要提供基本的服務(wù)注冊功能、服務(wù)分發(fā)功能等。同時,還需要提供各種資源,如:存儲服務(wù)資源(通過NLS、API等,提供存儲層的訪問接口)、日志服務(wù)資源等,給處理單元,讓其能夠方便的計算和處理。
?????????總體來說,因為HTTP的無狀態(tài)特性,以及不存在數(shù)據(jù)的存儲,邏輯層要做到同構(gòu)化是相對比較容易的,并且同構(gòu)化以后的運維也就非常容易了。
?????????最后,看看數(shù)據(jù)存儲層。
?存儲層是運維設(shè)計中最難的一部分。因為根據(jù)不同的業(yè)務(wù)需要,可能提供不同的存儲引擎,而不同的存儲引擎實現(xiàn)的機理和方式可能完全不一樣。比如,為了保證數(shù)據(jù)的有效性和一致性,有些存儲系統(tǒng)需要使用事務(wù);而有些業(yè)務(wù),可能為了追求高效,可能會犧牲一些數(shù)據(jù)的一致性,而提供快速的KV查詢等等。
?????????因此,數(shù)據(jù)存儲層的異構(gòu)性就是運維設(shè)計中亟待解決的問題。
?????????一種比較理想的方式,就是讓各個存儲系統(tǒng),隱藏內(nèi)部的實現(xiàn),對外提供簡單的訪問接口。而在系統(tǒng)內(nèi)部,通過meta server、data assemble server、status manager、message queue等管理單元來管理數(shù)據(jù)存儲單元。當(dāng)然,這只是其中一種方式。也可以利用mysql類似的主從級聯(lián)方式來管理,這種方式也是可行的。
?????????數(shù)據(jù)存儲系統(tǒng)的設(shè)計,也沒有一個固定的規(guī)范(比如:Big Table、Cassandra、Oracle數(shù)據(jù)庫集群等),所以運維的設(shè)計需要在系統(tǒng)中來充分考慮。上述圖示只是提供了一種簡單的設(shè)計方案。
?????????好了,有了多個層次的詳細(xì)分析以后,上一層次調(diào)用下一層次,就直接通過固定的地址進(jìn)行訪問即可。
?????????不過有一個問題就是,如果我們的Virtual Server是一個單點,出現(xiàn)故障后,該層就不能提供服務(wù)。這個是我們不可接受的。怎么辦呢?
?????????要解決這樣的問題,就是利用冗余。我們可以將我們的服務(wù)劃分為多個組,分別由多個VS來管理。上層調(diào)用下層的時候,通過一定的選擇策略來選擇即可。這樣,如果服務(wù)出現(xiàn)問題,我們就能通過冗余策略,將請求冗余到其他組上。
?????????我們來看一個實例:
???????用戶通過域名訪問我們的服務(wù),DNS通過訪問IP解析,返回對應(yīng)訪問層的IP地址。訪問層將請求轉(zhuǎn)發(fā)到對應(yīng)的邏輯處理組,邏輯處理組從不同的存儲系統(tǒng)里獲取數(shù)據(jù),并返回處理結(jié)果。
?????????通過以上的分析,我們通過原有的一些技術(shù)手段,可以做到比較好的自動化運維的方式。不過這種運維方式也不是完全智能的。有些時候也需要人工的參與。最難的一點就是存儲系統(tǒng)的設(shè)計和實現(xiàn)。如果要完全的自動化的話,是一件比較難的事情。
?????????說明:以上的描述是一個比較理想化的模型,要真正實現(xiàn)這種模型,需要很多的輔助手段,并且需要搭建很多基礎(chǔ)設(shè)施。可能會遇到我們沒有提到的很多的實際問題,比如:跨機房網(wǎng)絡(luò)傳輸延遲、服務(wù)間隔離性、網(wǎng)卡帶寬限制、服務(wù)的存活監(jiān)控等等。因此,在具體實施的時候,需要仔細(xì)分析和考慮。
?
轉(zhuǎn)自?http://stblog.baidu-tech.com/?p=1643
總結(jié)
以上是生活随笔為你收集整理的蚂蚁变大象:浅谈常规网站是如何从小变大的(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cent os数据库安装
- 下一篇: S3 Texture Compressi