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