分布式系統(tǒng),你真的了解嗎?
我們邀請騰訊互娛研發(fā)部高級工程師韓偉,分享他所理解的分布式系統(tǒng)。由于內(nèi)容較多,將分三篇進行講述,本期***篇先來看看他眼中的分布式系統(tǒng)究竟是什么吧。
承載量是分布式系統(tǒng)存在的原因
當(dāng)一個互聯(lián)網(wǎng)業(yè)務(wù)獲得大眾歡迎的時候,最顯著碰到的技術(shù)問題,就是服務(wù)器非常繁忙。當(dāng)每天有1000萬個用戶訪問你的網(wǎng)站時,無論你使用什么樣的服務(wù)器硬件,都不可能只用一臺機器就承載的了。因此,在互聯(lián)網(wǎng)程序員解決服務(wù)器端問題的時候,必須要考慮如何使用多臺服務(wù)器,為同一種互聯(lián)網(wǎng)應(yīng)用提供服務(wù),這就是所謂“分布式系統(tǒng)”的來源。
然而,大量用戶訪問同一個互聯(lián)網(wǎng)業(yè)務(wù),所造成的問題并不簡單。從表面上看,要能滿足很多用戶來自互聯(lián)網(wǎng)的請求,最基本的需求就是所謂性能需求:用戶反應(yīng)網(wǎng)頁打開很慢,或者網(wǎng)游中的動作很卡等等。而這些對于“服務(wù)速度”的要求,實際上包含的部分卻是以下幾個:高吞吐、高并發(fā)、低延遲和負(fù)載均衡。
高吞吐,意味著你的系統(tǒng),可以同時承載大量的用戶使用。這里關(guān)注的整個系統(tǒng)能同時服務(wù)的用戶數(shù)。這個吞吐量肯定是不可能用單臺服務(wù)器解決的,因此需要多臺服務(wù)器協(xié)作,才能達(dá)到所需要的吞吐量。而在多臺服務(wù)器的協(xié)作中,如何才能有效的利用這些服務(wù)器,不致于其中某一部分服務(wù)器成為瓶頸,從而影響整個系統(tǒng)的處理能力,這就是一個分布式系統(tǒng),在架構(gòu)上需要仔細(xì)權(quán)衡的問題。
高并發(fā)是高吞吐的一個延伸需求。當(dāng)我們在承載海量用戶的時候,我們當(dāng)然希望每個服務(wù)器都能盡其所能的工作,而不要出現(xiàn)無謂的消耗和等待的情況。然而,軟件系統(tǒng)并不是簡單的設(shè)計,就能對同時處理多個任務(wù),做到“盡量多”的處理。很多時候,我們的程序會因為要選擇處理哪個任務(wù),而導(dǎo)致額外的消耗。這也是分布式系統(tǒng)解決的問題。
低延遲對于人數(shù)***的服務(wù)來說不算什么問題。然而,如果我們需要在大量用戶訪問的時候,也能很快的返回計算結(jié)果,這就要困難的多。因為除了大量用戶訪問可能造成請求在排隊外,還有可能因為排隊的長度太長,導(dǎo)致內(nèi)存耗盡、帶寬占滿等空間性的問題。
如果因為排隊失敗而采取重試的策略,則整個延遲會變的更高。所以分布式系統(tǒng)會采用很多請求分揀和分發(fā)的做法,盡快的讓更多的服務(wù)器來出來用戶的請求。但是,由于一個數(shù)量龐大的分布式系統(tǒng),必然需要把用戶的請求經(jīng)過多次的分發(fā),整個延遲可能會因為這些分發(fā)和轉(zhuǎn)交的操作,變得更高,所以分布式系統(tǒng)除了分發(fā)請求外,還要盡量想辦法減少分發(fā)的層次數(shù),以便讓請求能盡快的得到處理。
由于互聯(lián)網(wǎng)業(yè)務(wù)的用戶來自全世界,因此在物理空間上可能來自各種不同延遲的網(wǎng)絡(luò)和線路,在時間上也可能來自不同的時區(qū),所以要有效的應(yīng)對這種用戶來源的復(fù)雜性,就需要把多個服務(wù)器部署在不同的空間來提供服務(wù)。同時,我們也需要讓同時發(fā)生的請求,有效的讓多個不同服務(wù)器承載。所謂的負(fù)載均衡,就是分布式系統(tǒng)與生俱來需要完成的功課。
由于分布式系統(tǒng),幾乎是解決互聯(lián)網(wǎng)業(yè)務(wù)承載量問題的最基本方法,所以作為一個服務(wù)器端程序員,掌握分布式系統(tǒng)技術(shù)就變得異常重要了。然而,分布式系統(tǒng)的問題,并非是學(xué)會用幾個框架和使用幾個庫,就能輕易解決的,因為當(dāng)一個程序在一個電腦上運行,變成了又無數(shù)個電腦上同時協(xié)同運行,在開發(fā)、運維上都會帶來很大的差別。
分布式系統(tǒng)提高承載量的基本手段
分層模型(路由、代理)
使用多態(tài)服務(wù)器來協(xié)同完成計算任務(wù),最簡單的思路就是,讓每個服務(wù)器都能完成全部的請求,然后把請求隨機的發(fā)給任何一個服務(wù)器處理。最早期的互聯(lián)網(wǎng)應(yīng)用中,DNS輪詢就是這樣的做法:當(dāng)用戶輸入一個域名試圖訪問某個網(wǎng)站,這個域名會被解釋成多個IP地址中的一個,隨后這個網(wǎng)站的訪問請求,就被發(fā)往對應(yīng)IP的服務(wù)器了,這樣多個服務(wù)器(多個IP地址)就能一起解決處理大量的用戶請求。
然而,單純的請求隨機轉(zhuǎn)發(fā),并不能解決一切問題。比如我們很多互聯(lián)網(wǎng)業(yè)務(wù),都是需要用戶登錄的。在登錄某一個服務(wù)器后,用戶會發(fā)起多個請求,如果我們把這些請求隨機的轉(zhuǎn)發(fā)到不同的服務(wù)器上,那么用戶登錄的狀態(tài)就會丟失,造成一些請求處理失敗。
簡單的依靠一層服務(wù)轉(zhuǎn)發(fā)是不夠的,所以我們會增加一批服務(wù)器,這些服務(wù)器會根據(jù)用戶的Cookie,或者用戶的登錄憑據(jù),來再次轉(zhuǎn)發(fā)給后面具體處理業(yè)務(wù)的服務(wù)器。
除了登錄的需求外,我們還發(fā)現(xiàn),很多數(shù)據(jù)是需要數(shù)據(jù)庫來處理的,而我們的這些數(shù)據(jù)往往都只能集中到一個數(shù)據(jù)庫中,否則在查詢的時候就會丟失其他服務(wù)器上存放的數(shù)據(jù)結(jié)果。所以往往我們還會把數(shù)據(jù)庫單獨出來成為一批專用的服務(wù)器。
至此,我們就會發(fā)現(xiàn),一個典型的三層結(jié)構(gòu)出現(xiàn)了:接入、邏輯、存儲。然而,這種三層結(jié)果,并不就能包醫(yī)百病。
例如,當(dāng)我們需要讓用戶在線互動(網(wǎng)游就是典型) ,那么分割在不同邏輯服務(wù)器上的在線狀態(tài)數(shù)據(jù),是無法知道對方的,這樣我們就需要專門做一個類似互動服務(wù)器的專門系統(tǒng),讓用戶登錄的時候,也同時記錄一份數(shù)據(jù)到它那里,表明某個用戶登錄在某個服務(wù)器上,而所有的互動操作,要先經(jīng)過這個互動服務(wù)器,才能正確的把消息轉(zhuǎn)發(fā)到目標(biāo)用戶的服務(wù)器上。
又例如,當(dāng)我們在使用網(wǎng)上論壇(BBS)系統(tǒng)的時候,我們發(fā)的文章,不可能只寫入一個數(shù)據(jù)庫里,因為太多人的閱讀請求會拖死這個數(shù)據(jù)庫。我們常常會按論壇板塊來寫入不同的數(shù)據(jù)庫,又或者是同時寫入多個數(shù)據(jù)庫。這樣把文章數(shù)據(jù)分別存放到不同的服務(wù)器上,才能應(yīng)對大量的操作請求。
然而,用戶在讀取文章的時候,就需要有一個專門的程序,去查找具體文章在哪一個服務(wù)器上,這時候我們就要架設(shè)一個專門的代理層,把所有的文章請求先轉(zhuǎn)交給它,由它按照我們預(yù)設(shè)的存儲計劃,去找對應(yīng)的數(shù)據(jù)庫獲取數(shù)據(jù)。
根據(jù)上面的例子來看,分布式系統(tǒng)雖然具有三層典型的結(jié)構(gòu),但是實際上往往不止有三層,而是根據(jù)業(yè)務(wù)需求,會設(shè)計成多個層次的。為了把請求轉(zhuǎn)交給正確的進程處理,我們而設(shè)計很多專門用于轉(zhuǎn)發(fā)請求的進程和服務(wù)器。這些進程我們常常以Proxy或者Router來命名,一個多層結(jié)構(gòu)常常會具備各種各樣的Proxy進程。
這些代理進程,很多時候都是通過TCP來連接前后兩端。然而,TCP雖然簡單,但是卻會有故障后不容易恢復(fù)的問題。而且TCP的網(wǎng)絡(luò)編程,也是有點復(fù)雜的。——所以,人們設(shè)計出更好進程間通訊機制:消息隊列。
盡管通過各種Proxy或者Router進程能組建出強大的分布式系統(tǒng),但是其管理的復(fù)雜性也是非常高的。所以人們在分層模式的基礎(chǔ)上,想出了更多的方法,來讓這種分層模式的程序變得更簡單高效的方法。
并發(fā)模型(多線程、異步)
當(dāng)我們在編寫服務(wù)器端程序時,我們會明確的知道,大部分的程序,都是會處理同時到達(dá)的多個請求的。因此我們不能好像HelloWorld那么簡單的,從一個簡單的輸入計算出輸出來。因為我們會同時獲得很多個輸入,需要返回很多個輸出。
在這些處理的過程中,往往我們還會碰到需要“等待”或“阻塞”的情況,比如我們的程序要等待數(shù)據(jù)庫處理結(jié)果,等待向另外一個進程請求結(jié)果等等……如果我們把請求一個挨著一個的處理,那么這些空閑的等待時間將白白浪費,造成用戶的響應(yīng)延時增加,以及整體系統(tǒng)的吞吐量極度下降。
所以在如何同時處理多個請求的問題上,業(yè)界有2個典型的方案。一種是多線程,一種是異步。在早期的系統(tǒng)中,多線程或多進程是最常用的技術(shù)。這種技術(shù)的代碼編寫起來比較簡單,因為每個線程中的代碼都肯定是按先后順序執(zhí)行的。但是由于同時運行著多個線程,所以你無法保障多個線程之間的代碼的先后順序。
這對于需要處理同一個數(shù)據(jù)的邏輯來說,是一個非常嚴(yán)重的問題,最簡單的例子就是顯示某個新聞的閱讀量。兩個++操作同時運行,有可能結(jié)果只加了1,而不是2。所以多線程下,我們常常要加很多數(shù)據(jù)的鎖,而這些鎖又反過來可能導(dǎo)致線程的死鎖。
因此異步回調(diào)模型在隨后比多線程更加流行,除了多線程的死鎖問題外,異步還能解決多線程下,線程反復(fù)切換導(dǎo)致不必要的開銷的問題:每個線程都需要一個獨立的??臻g,在多線程并行運行的時候,這些棧的數(shù)據(jù)可能需要來回的拷貝,這額外消耗了CPU。
同時由于每個線程都需要占用??臻g,所以在大量線程存在的時候,內(nèi)存的消耗也是巨大的。而異步回調(diào)模型則能很好的解決這些問題,不過異步回調(diào)更像是“手工版”的并行處理,需要開發(fā)者自己去實現(xiàn)如何“并行”的問題。
異步回調(diào)基于非阻塞的I/O操作(網(wǎng)絡(luò)和文件),這樣我們就不用在調(diào)用讀寫函數(shù)的時候“卡”在那一句函數(shù)調(diào)用,而是立刻返回“有無數(shù)據(jù)”的結(jié)果。而Linux的epoll技術(shù),則利用底層內(nèi)核的機制,讓我們可以快速的“查找”到有數(shù)據(jù)可以讀寫的連接\文件。由于每個操作都是非阻塞的,所以我們的程序可以只用一個進程,就處理大量并發(fā)的請求。
因為只有一個進程,所以所有的數(shù)據(jù)處理,其順序都是固定的,不可能出現(xiàn)多線程中,兩個函數(shù)的語句交錯執(zhí)行的情況,因此也不需要各種“鎖”。從這個角度看,異步非阻塞的技術(shù),是大大簡化了開發(fā)的過程。由于只有一個線程,也不需要有線程切換之類的開銷,所以異步非阻塞成為很多對吞吐量、并發(fā)有較高要求的系統(tǒng)***。
緩沖技術(shù)
在互聯(lián)網(wǎng)服務(wù)中,大部分的用戶交互,都是需要立刻返回結(jié)果的,所以對于延遲有一定的要求。而類似網(wǎng)絡(luò)游戲之類服務(wù),延遲更是要求縮短到幾十毫秒以內(nèi)。所以為了降低延遲,緩沖是互聯(lián)網(wǎng)服務(wù)中最常見的技術(shù)之一。
早期的WEB系統(tǒng)中,如果每個HTTP請求的處理,都去數(shù)據(jù)庫(MySQL)讀寫一次,那么數(shù)據(jù)庫很快就會因為連接數(shù)占滿而停止響應(yīng)。因為一般的數(shù)據(jù)庫,支持的連接數(shù)都只有幾百,而WEB的應(yīng)用的并發(fā)請求,輕松能到幾千。這也是很多設(shè)計不良的網(wǎng)站人一多就卡死的最直接原因。
為了盡量減少對數(shù)據(jù)庫的連接和訪問,人們設(shè)計了很多緩沖系統(tǒng)——把從數(shù)據(jù)庫中查詢的結(jié)果存放到更快的設(shè)施上,如果沒有相關(guān)聯(lián)的修改,就直接從這里讀。
最典型的WEB應(yīng)用緩沖系統(tǒng)是Memcache。由于PHP本身的線程結(jié)構(gòu),是不帶狀態(tài)的。早期PHP本身甚至連操作“堆”內(nèi)存的方法都沒有,所以那些持久的狀態(tài),就一定要存放到另外一個進程里。而Memcache就是一個簡單可靠的存放臨時狀態(tài)的開源軟件。
很多PHP應(yīng)用現(xiàn)在的處理邏輯,都是先從數(shù)據(jù)庫讀取數(shù)據(jù),然后寫入Memcache;當(dāng)下次請求來的時候,先嘗試從Memcache里面讀取數(shù)據(jù),這樣就有可能大大減少對數(shù)據(jù)庫的訪問。
然而Memcache本身是一個獨立的服務(wù)器進程,這個進程自身并不帶特別的集群功能。也就是說這些Memcache進程,并不能直接組建成一個統(tǒng)一的集群。如果一個Memcache不夠用,我們就要手工用代碼去分配,哪些數(shù)據(jù)應(yīng)該去哪個Memcache進程。——這對于真正的大型分布式網(wǎng)站來說,管理一個這樣的緩沖系統(tǒng),是一個很繁瑣的工作。
因此人們開始考慮設(shè)計一些更高效的緩沖系統(tǒng):從性能上來說,Memcache的每筆請求,都要經(jīng)過網(wǎng)絡(luò)傳輸,才能去拉取內(nèi)存中的數(shù)據(jù)。這無疑是有一點浪費的,因為請求者本身的內(nèi)存,也是可以存放數(shù)據(jù)的。——這就是促成了很多利用請求方內(nèi)存的緩沖算法和技術(shù),其中最簡單的就是使用LRU算法,把數(shù)據(jù)放在一個哈希表結(jié)構(gòu)的堆內(nèi)存中。
而Memcache的不具備集群功能,也是一個用戶的痛點。于是很多人開始設(shè)計,如何讓數(shù)據(jù)緩存分不到不同的機器上。最簡單的思路是所謂讀寫分離,也就是緩存每次寫,都寫到多個緩沖進程上記錄,而讀則可以隨機讀任何一個進程。在業(yè)務(wù)數(shù)據(jù)有明顯的讀寫不平衡差距上,效果是非常好的。
然而,并不是所有的業(yè)務(wù)都能簡單的用讀寫分離來解決問題,比如一些在線互動的互聯(lián)網(wǎng)業(yè)務(wù),比如社區(qū)、游戲。這些業(yè)務(wù)的數(shù)據(jù)讀寫頻率并沒很大的差異,而且也要求很高的延遲。因此人們又再想辦法,把本地內(nèi)存和遠(yuǎn)端進程的內(nèi)存緩存結(jié)合起來使用,讓數(shù)據(jù)具備兩級緩存。
同時,一個數(shù)據(jù)不在同時的復(fù)制存在所有的緩存進程上,而是按一定規(guī)律分布在多個進程上。——這種分布規(guī)律使用的算法,***的就是所謂“一致性哈希”。這種算法的好處是,當(dāng)某一個進程失效掛掉,不需要把整個集群中所有的緩存數(shù)據(jù),都重新修改一次位置。
你可以想象一下,如果我們的數(shù)據(jù)緩存分布,是用簡單的以數(shù)據(jù)的ID對進程數(shù)取模,那么一旦進程數(shù)變化,每個數(shù)據(jù)存放的進程位置都可能變化,這對于服務(wù)器的故障容忍是不利的。
Orcale公司旗下有一款叫Coherence的產(chǎn)品,是在緩存系統(tǒng)上設(shè)計比較好的。這個產(chǎn)品是一個商業(yè)產(chǎn)品,支持利用本地內(nèi)存緩存和遠(yuǎn)程進程緩存協(xié)作。集群進程是完全自管理的,還支持在數(shù)據(jù)緩存所在進程,進行用戶定義的計算(處理器功能),這就不僅僅是緩存了,還是一個分布式的計算系統(tǒng)。
存儲技術(shù)(NoSQL)
相信CAP理論大家已經(jīng)耳熟能詳,然而在互聯(lián)發(fā)展的早期,大家都還在使用MySQL的時候,如何讓數(shù)據(jù)庫存放更多的數(shù)據(jù),承載更多的連接,很多團隊都是絞盡腦汁。甚至于有很多業(yè)務(wù),主要的數(shù)據(jù)存儲方式是文件,數(shù)據(jù)庫反而變成是輔助的設(shè)施了。
然而,當(dāng)NoSQL興起,大家突然發(fā)現(xiàn),其實很多互聯(lián)網(wǎng)業(yè)務(wù),其數(shù)據(jù)格式是如此的簡單,很多時候根部不需要關(guān)系型數(shù)據(jù)庫那種復(fù)雜的表格。對于索引的要求往往也只是根據(jù)主索引搜索。而更復(fù)雜的全文搜索,本身數(shù)據(jù)庫也做不到。所以現(xiàn)在相當(dāng)多的高并發(fā)的互聯(lián)網(wǎng)業(yè)務(wù),***NoSQL來做存儲設(shè)施。最早的NoSQL數(shù)據(jù)庫有MangoDB等,現(xiàn)在***的似乎就是Redis了。甚至有些團隊,把Redis也當(dāng)成緩沖系統(tǒng)的一部分,實際上也是認(rèn)可Redis的性能優(yōu)勢。
NoSQL除了更快、承載量更大以外,更重要的特點是,這種數(shù)據(jù)存儲方式,只能按照一條索引來檢索和寫入。這樣的需求約束,帶來了分布上的好處,我們可以按這條主索引,來定義數(shù)據(jù)存放的進程(服務(wù)器)。這樣一個數(shù)據(jù)庫的數(shù)據(jù),就能很方便的存放在不同的服務(wù)器上。在分布式系統(tǒng)的必然趨勢下,數(shù)據(jù)存儲層終于也找到了分布的方法。
分布式系統(tǒng)在可管理性上造成的問題
分布式系統(tǒng)并不是簡單的把一堆服務(wù)器一起運行起來就能滿足需求的。對比單機或少量服務(wù)器的集群,有一些特別需要解決的問題等待著我們。
硬件故障率
所謂分布式系統(tǒng),肯定就不是只有一臺服務(wù)器。假設(shè)一臺服務(wù)器的可靠運行時間是1%,那么當(dāng)你有100臺服務(wù)器的時候,那就幾乎總有一臺是在故障的。雖然這個比方不一定很準(zhǔn)確,但是,當(dāng)你的系統(tǒng)所涉及的硬件越來越多,硬件的故障也會從偶然事件變成一個必然事件。
一般我們在寫功能代碼的時候,是不會考慮到硬件故障的時候應(yīng)該怎么辦的。而如果在編寫分布式系統(tǒng)的時候,就一定需要面對這個問題了。否則,很可能只有一臺服務(wù)器出故障,整個數(shù)百臺服務(wù)器的集群都工作不正常了。
除了服務(wù)器自己的內(nèi)存、硬盤等故障,服務(wù)器之間的網(wǎng)絡(luò)線路故障更加常見。而且這種故障還有可能是偶發(fā)的,或者是會自動恢復(fù)的。面對這種問題,如果只是簡單的把“出現(xiàn)故障”的機器剔除出去,那還是不夠的。因為網(wǎng)絡(luò)可能過一會兒就又恢復(fù)了,而你的集群可能因為這一下的臨時故障,丟失了過半的處理能力。
如何讓分布式系統(tǒng),在各種可能隨時出現(xiàn)故障的情況下,盡量的自動維護和維持對外服務(wù),成為了編寫程序就要考慮的問題。由于要考慮到這種故障的情況,所以我們在設(shè)計架構(gòu)的時候,也要有意識的預(yù)設(shè)一些冗余、自我維護的功能。這些都不是產(chǎn)品上的業(yè)務(wù)需求,完全就是技術(shù)上的功能需求。能否在這方面提出對的需求,然后正確的實現(xiàn),是服務(wù)器端程序員最重要的職責(zé)之一。
資源利用率優(yōu)化
在分布式系統(tǒng)的集群,包含了很多個服務(wù)器,當(dāng)這樣一個集群的硬件承載能力到達(dá)極限的時候,最自然的想法就是增加更多的硬件。然而,一個軟件系統(tǒng)不是那么容易就可以通過“增加”硬件來提高承載性能的。因為軟件在多個服務(wù)器上的工作,是需要有復(fù)雜細(xì)致的協(xié)調(diào)工作。在對一個集群擴容的時候,我們往往會要停掉整個集群的服務(wù),然后修改各種配置,***才能重新啟動一個加入了新的服務(wù)器的集群。
由于在每個服務(wù)器的內(nèi)存里,都可能會有一些用戶使用的數(shù)據(jù),所以如果冒然在運行的時候,就試圖修改集群中提供服務(wù)的配置,很可能會造成內(nèi)存數(shù)據(jù)的丟失和錯誤。因此,運行時擴容在對無狀態(tài)的服務(wù)上,是比較容易的,比如增加一些Web服務(wù)器。但如果是在有狀態(tài)的服務(wù)上,比如網(wǎng)絡(luò)游戲,幾乎是不可能進行簡單的運行時擴容的。
分布式集群除了擴容,還有縮容的需求。當(dāng)用戶人數(shù)下降,服務(wù)器硬件資源出現(xiàn)空閑的時候,我們往往需要這些空閑的資源能利用起來,放到另外一些新的服務(wù)集群里去??s容和集群中有故障需要容災(zāi)有一定類似之處,區(qū)別是縮容的時間點和目標(biāo)是可預(yù)期的。
由于分布式集群中的擴容、縮容,以及希望盡量能在線操作,這導(dǎo)致了非常復(fù)雜的技術(shù)問題需要處理,比如集群中互相關(guān)聯(lián)的配置如何正確高效的修改、如何對有狀態(tài)的進程進行操作、如何在擴容縮容的過程中保證集群中節(jié)點之間通信的正常。作為服務(wù)器端程序員,會需要花費大量的經(jīng)歷,來對多個進程的集群狀態(tài)變化,造成的一系列問題進行專門的開發(fā)。
軟件服務(wù)內(nèi)容更新
現(xiàn)在都流行用敏捷開發(fā)模式中的“迭代”,來表示一個服務(wù)不斷的更新程序,滿足新的需求,修正BUG。如果我們僅僅管理一臺服務(wù)器,那么更新這一臺服務(wù)器上的程序,是非常簡單的:只要把軟件包拷貝過去,然后修改下配置就好。但是如果你要對成百上千的服務(wù)器去做同樣的操作,就不可能每臺服務(wù)器登錄上去處理。
服務(wù)器端的程序批量安裝部署工具,是每個分布式系統(tǒng)開發(fā)者都需要的。然而,我們的安裝工作除了拷貝二進制文件和配置文件外,還會有很多其他的操作。比如打開防火墻、建立共享內(nèi)存文件、修改數(shù)據(jù)庫表結(jié)構(gòu)、改寫一些數(shù)據(jù)文件等等……甚至有一些還要在服務(wù)器上安裝新的軟件。
如果我們在開發(fā)服務(wù)器端程序的時候,就考慮到軟件更新、版本升級的問題,那么我們對于配置文件、命令行參數(shù)、系統(tǒng)變量的使用,就會預(yù)先做一定的規(guī)劃,這能讓安裝部署的工具運行更快,可靠性更高。
除了安裝部署的過程,還有一個重要的問題,就是不同版本間數(shù)據(jù)的問題。我們在升級版本的時候,舊版本程序生成的一些持久化數(shù)據(jù),一般都是舊的數(shù)據(jù)格式的;而我們升級版本中如果涉及修改了數(shù)據(jù)格式,比如數(shù)據(jù)表結(jié)果,那么這些舊格式的數(shù)據(jù),都要轉(zhuǎn)換改寫成新版本的數(shù)據(jù)格式才行。
這導(dǎo)致了我們在設(shè)計數(shù)據(jù)結(jié)構(gòu)的時候,就要考慮清楚這些表格的結(jié)構(gòu),是用最簡單直接的表達(dá)方式,來讓將來的修改更簡單;還是一早就預(yù)計到修改的范圍,專門預(yù)設(shè)一些字段,或者使用其他形式存放數(shù)據(jù)。
除了持久化數(shù)據(jù)以外,如果存在客戶端程序(如受擊APP),這些客戶端程序的升級往往不能和服務(wù)器同步,如果升級的內(nèi)容包含了通信協(xié)議的修改,這就造成了我們必須為不同的版本部署不同的服務(wù)器端系統(tǒng)的問題。為了避免同時維護多套服務(wù)器,我們在軟件開發(fā)的時候,往往傾向于所謂“版本兼容”的協(xié)議定義方式。而怎樣設(shè)計的協(xié)議才能有很好的兼容性,又是服務(wù)器端程序需要仔細(xì)考慮的問題。
數(shù)據(jù)統(tǒng)計和決策
一般來說,分布式系統(tǒng)的日志數(shù)據(jù),都是被集中到一起,然后統(tǒng)一進行統(tǒng)計的。然而,當(dāng)集群的規(guī)模到一定程度的時候,這些日志的數(shù)據(jù)量會變得非??植?。很多時候,統(tǒng)計一天的日志量,要消耗計算機運行一天以上的時間。所以,日志統(tǒng)計這項工作,也變成一門非常專業(yè)的活動。
經(jīng)典的分布式統(tǒng)計模型,有Google的Map Reduce模型。這種模型既有靈活性,也能利用大量服務(wù)器進行統(tǒng)計工作。但是缺點是易用性往往不夠好,因為這些數(shù)據(jù)的統(tǒng)計和我們常見的SQL數(shù)據(jù)表統(tǒng)計有非常大的差異,所以我們***還是常常把數(shù)據(jù)丟到MySQL里面去做更細(xì)層面的統(tǒng)計。
由于分布式系統(tǒng)日志數(shù)量的龐大,以及日志復(fù)雜程度的提高。我們變得必須要掌握類似Map Reduce技術(shù),才能真正的對分布式系統(tǒng)進行數(shù)據(jù)統(tǒng)計。而且我們還需要想辦法提高統(tǒng)計工作的工作效率。
未完待續(xù)……