1、單服務(wù)器配置
萬里征途總是從第一步開始的,構(gòu)建一個(gè)復(fù)雜系統(tǒng)也是如此。我們從簡單的部分著手,先讓所有的功能都在一個(gè)服務(wù)器上運(yùn)行。圖1-1展示了如何配置單臺(tái)服務(wù)器,讓一切都在其上運(yùn)行,包括Web應(yīng)用、數(shù)據(jù)庫、緩存等。
研究請(qǐng)求流和流量源頭有助于我們理解這個(gè)配置。我們先來看請(qǐng)求流(如圖1-2所示)。
圖片
圖片
1.用戶通過輸入域名(例如api.mysite.com)來訪問網(wǎng)站。通常,域名系統(tǒng)(DNS)是由第三方提供的付費(fèi)服務(wù),它并不是由我們的服務(wù)器來托管的。
2.IP地址被返回給網(wǎng)頁瀏覽器或者移動(dòng)應(yīng)用。在圖1-2所示的例子中,被返回的IP地址是15.125.23.214。
3.一旦獲知IP地址,HTTP請(qǐng)求就被直接發(fā)送給Web服務(wù)器。
4.Web服務(wù)器返回HTML頁面或者JSON響應(yīng)來渲染頁面。
接下來,我們研究一下流量源頭。Web服務(wù)器的流量有兩個(gè)源頭:Web應(yīng)用和移動(dòng)應(yīng)用。
—Web應(yīng)用:它運(yùn)用服務(wù)器端語言(Java、Python等)來處理業(yè)務(wù)邏輯、數(shù)據(jù)存儲(chǔ)等;它還使用客戶端語言(HTML和JavaScript)來展示內(nèi)容。
—移動(dòng)應(yīng)用:HTTP是移動(dòng)應(yīng)用與Web服務(wù)器之間的通信協(xié)議。而JSON(JavaScript Object Notation)因其十分簡單而被廣泛用作數(shù)據(jù)傳輸時(shí)的API響應(yīng)格式。以下是一個(gè)JSON格式的API響應(yīng)例子。
GET/users/12–獲取id=12的用戶對(duì)象 { "id":12, "firstName'":"John", "lastName":"Smith", "address":{ "streetAddress":"212ndStreet", "city":"NewYork", "state'":"NY", "postalCode":10021 }, "phoneNumbers":[ "212555-1234", "646555-4567" ] }
2、數(shù)據(jù)庫
隨著用戶基數(shù)的增長,一臺(tái)服務(wù)器已經(jīng)無法滿足需求,我們需要多臺(tái)服務(wù)器:一臺(tái)用于處理Web應(yīng)用/移動(dòng)應(yīng)用的流量,另一臺(tái)用作數(shù)據(jù)庫(如圖1-3所示)。把處理Web應(yīng)用/移動(dòng)應(yīng)用流量(網(wǎng)絡(luò)層)的服務(wù)器與數(shù)據(jù)庫(數(shù)據(jù)層)服務(wù)器分開,我們就可以對(duì)它們分別進(jìn)行擴(kuò)展。
圖片
使用何種數(shù)據(jù)庫
你可以選擇傳統(tǒng)的關(guān)系型數(shù)據(jù)庫,也可以選擇非關(guān)系型數(shù)據(jù)庫。我們來看看它們的區(qū)別。
關(guān)系型數(shù)據(jù)庫通常也叫作關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(RDBMS)或者SQL數(shù)據(jù)庫,其中最流行的有MySQL、Oracle、PostgreSQL等。關(guān)系型數(shù)據(jù)庫通過表和行來表示和存儲(chǔ)數(shù)據(jù)。你可以使用SQL對(duì)不同的數(shù)據(jù)庫表執(zhí)行連接(join)操作。
非關(guān)系型數(shù)據(jù)庫又叫作NoSQL數(shù)據(jù)庫。流行的非關(guān)系型數(shù)據(jù)庫有CouchDB、Neo4j、Cassandra、HBase、Amazon DynamoDB等。它們可以分為四類:鍵值存儲(chǔ)、圖存儲(chǔ)、列存儲(chǔ)和文檔存儲(chǔ)。非關(guān)系型數(shù)據(jù)庫一般不支持連接操作。
對(duì)于大多數(shù)開發(fā)者而言,關(guān)系型數(shù)據(jù)庫是最好的選擇,因?yàn)樗鼈円呀?jīng)有40多年的歷史,而且一直表現(xiàn)不錯(cuò)。但如果它們無法滿足你的特殊使用場景要求,你就需要考慮關(guān)系型數(shù)據(jù)庫之外的選項(xiàng)。當(dāng)需要滿足如下條件時(shí),非關(guān)系型數(shù)據(jù)庫可能是一個(gè)正確的選擇:
—你的應(yīng)用只能接受非常低的延時(shí)。
—應(yīng)用中的數(shù)據(jù)是非結(jié)構(gòu)化的,或者根本沒有任何關(guān)系型數(shù)據(jù)。
—只需要序列化(JSON、XML、YAML等格式)和反序列化數(shù)據(jù)。
—需要存儲(chǔ)海量數(shù)據(jù)。
3、縱向擴(kuò)展 vs. 橫向擴(kuò)展
縱向擴(kuò)展也叫作向上擴(kuò)展,指的是提升服務(wù)器的能力(CPU、RAM等)。橫向擴(kuò)展也叫作向外擴(kuò)展,指的是為你的資源池添加更多服務(wù)器。
當(dāng)流量小的時(shí)候,縱向擴(kuò)展是一個(gè)很好的選擇,其主要優(yōu)勢是簡單。不過,它有一些重大局限。
—縱向擴(kuò)展是有硬性限制的,你不可能給一臺(tái)服務(wù)器無限添加CPU和內(nèi)存。
—縱向擴(kuò)展沒有故障轉(zhuǎn)移和冗余。一旦一臺(tái)服務(wù)器宕機(jī),網(wǎng)站/應(yīng)用也會(huì)隨著一起完全不可用。
由于縱向擴(kuò)展存在這些限制,因此對(duì)于大型應(yīng)用來說,采用橫向擴(kuò)展更合適一些。
在我們前面的設(shè)計(jì)中,用戶是直接連接到Web服務(wù)器的。一旦服務(wù)器離線,用戶就無法訪問網(wǎng)站了。還有一種場景是,非常多的用戶同時(shí)訪問Web服務(wù)器,達(dá)到了其負(fù)載上限,這時(shí)用戶就會(huì)普遍感受到網(wǎng)站響應(yīng)慢或者無法連上服務(wù)器。解決這些問題的最佳方法是使用負(fù)載均衡器。
4、負(fù)載均衡器
負(fù)載均衡器會(huì)把輸入流量均勻分配到負(fù)載均衡集里的各個(gè)Web服務(wù)器上。圖1-4展示了負(fù)載均衡器是怎么工作的。
如圖1-4所示,用戶可以直接連接該負(fù)載均衡器的公共IP地址。這樣設(shè)置后,Web服務(wù)器就再也不能被任何客戶端直接訪問了。為了提高安全性,服務(wù)器之間的通信使用私有IP地址。私有IP地址只可以被同一個(gè)網(wǎng)絡(luò)中的服務(wù)器訪問,在公網(wǎng)中是無法訪問的。負(fù)載均衡器和Web服務(wù)器之間使用私有IP地址來通信。
增加了負(fù)載均衡器和一臺(tái)Web服務(wù)器后,我們成功解決了網(wǎng)絡(luò)層的故障轉(zhuǎn)移問題,提升了網(wǎng)絡(luò)層的可用性。具體細(xì)節(jié)如下:
—如果服務(wù)器1離線,所有的流量都會(huì)被路由到服務(wù)器2,從而避免整個(gè)網(wǎng)站宕機(jī)。我們可以之后再將一臺(tái)新的“健康的”Web服務(wù)器添加到服務(wù)器池中,以平衡負(fù)載。
—如果網(wǎng)站流量增長非常快,兩臺(tái)服務(wù)器不足以處理這些流量,那么負(fù)載均衡器可以輕松地解決這個(gè)問題。只需要在服務(wù)器池中添加更多服務(wù)器,負(fù)載均衡器就會(huì)自動(dòng)將請(qǐng)求發(fā)給新加入的服務(wù)器。
圖片
現(xiàn)在網(wǎng)絡(luò)層看來已經(jīng)不錯(cuò)了,那么數(shù)據(jù)層呢?目前的設(shè)計(jì)方案中只有一個(gè)數(shù)據(jù)庫,所以無法支持?jǐn)?shù)據(jù)庫的故障轉(zhuǎn)移和冗余。數(shù)據(jù)庫復(fù)制是解決這些問題的常用技巧。
5、數(shù)據(jù)庫復(fù)制
根據(jù)維基百科上的定義,“在很多數(shù)據(jù)庫管理系統(tǒng)中,通常都可以利用原始數(shù)據(jù)庫(Master,主庫)和拷貝數(shù)據(jù)庫(Slave,從庫)之間的主從關(guān)系進(jìn)行數(shù)據(jù)庫復(fù)制?!?。
主庫通常只支持寫操作,從庫保存主庫的數(shù)據(jù)副本且僅支持讀操作。所有修改數(shù)據(jù)的指令,如插入、刪除或更新等,都必須發(fā)送給主庫來執(zhí)行。在大部分應(yīng)用中,對(duì)數(shù)據(jù)庫的讀操作遠(yuǎn)多于寫操作,因此系統(tǒng)中從庫的數(shù)量通常多于主庫的數(shù)量。圖1-5展示了一個(gè)主庫搭配多個(gè)從庫的例子。
數(shù)據(jù)庫復(fù)制有如下優(yōu)點(diǎn):
—性能更好。在主從模式下,所有的寫操作和更新操作都發(fā)生在主節(jié)點(diǎn)(主庫)上,而讀操作被分配到各個(gè)從節(jié)點(diǎn)(從庫),因此系統(tǒng)能并行處理更多的查詢,性能得到提升。
—可靠性高。如果有一臺(tái)數(shù)據(jù)庫服務(wù)器因自然災(zāi)害而損毀,比如遭遇臺(tái)風(fēng)或者地震,數(shù)據(jù)依然被完好保存,你不需要擔(dān)心數(shù)據(jù)會(huì)丟失,因?yàn)檫@些數(shù)據(jù)已經(jīng)被復(fù)制到處于不同地理位置的其他數(shù)據(jù)庫服務(wù)器中。
—可用性高。由于不同物理位置的從庫都復(fù)制了數(shù)據(jù),因此即使一臺(tái)數(shù)據(jù)庫服務(wù)器宕機(jī),你的網(wǎng)站依然可以運(yùn)行,因?yàn)榱硪慌_(tái)數(shù)據(jù)庫服務(wù)器里存儲(chǔ)了數(shù)據(jù)。
前面討論了負(fù)載均衡器是如何幫助提升系統(tǒng)可用性的,這里我們問一個(gè)同樣的問題:如果有數(shù)據(jù)庫服務(wù)器宕機(jī)了怎么辦?圖1-5所示的架構(gòu)可以應(yīng)對(duì)這種情況。
—如果只有一個(gè)從庫,而它宕機(jī)了,則系統(tǒng)暫時(shí)會(huì)將讀操作路由至主庫。一旦發(fā)現(xiàn)有從庫宕機(jī),就會(huì)有一個(gè)新的從庫來替代它。要是有多個(gè)從庫可用,讀操作會(huì)被重定向到其他正常工作的從庫上;同樣,也會(huì)有一個(gè)新的數(shù)據(jù)庫服務(wù)器來替代宕機(jī)的那個(gè)。
—如果主庫宕機(jī),會(huì)有一個(gè)從庫被推選為新的主庫。所有的數(shù)據(jù)庫操作會(huì)暫時(shí)在新的主庫上執(zhí)行。另一個(gè)從庫會(huì)替代原來的從庫并立即開始復(fù)制數(shù)據(jù)。在生產(chǎn)環(huán)境中,因?yàn)閺膸斓臄?shù)據(jù)不一定是最新的,所以推選一個(gè)新的主庫會(huì)更麻煩。缺失的數(shù)據(jù)需要通過運(yùn)行數(shù)據(jù)恢復(fù)腳本來補(bǔ)全。盡管還有別的數(shù)據(jù)復(fù)制方式可以解決數(shù)據(jù)缺失問題,比如多主復(fù)制或者循環(huán)復(fù)制,但是它們的設(shè)置更加復(fù)雜,本書不對(duì)這些內(nèi)容進(jìn)行討論。感興趣的讀者可以進(jìn)一步閱讀相關(guān)參考資料。
圖1-6展示了添加了負(fù)載均衡器和數(shù)據(jù)庫復(fù)制之后的系統(tǒng)設(shè)計(jì)方案。
圖片
我們再來看一下現(xiàn)在的設(shè)計(jì):
—用戶從DNS獲取負(fù)載均衡器的IP地址。
—用戶通過這個(gè)IP地址連接負(fù)載均衡器。
—HTTP請(qǐng)求被轉(zhuǎn)發(fā)到服務(wù)器1或者服務(wù)器2上。
—Web服務(wù)器在從庫中讀取用戶數(shù)據(jù)。
—Web服務(wù)器把所有修改數(shù)據(jù)的操作請(qǐng)求都轉(zhuǎn)發(fā)到主庫上,包括寫、更新和刪除操作。
現(xiàn)在我們對(duì)于網(wǎng)絡(luò)層和數(shù)據(jù)層都有了一定的理解,接下來可以提升加載和響應(yīng)速度了??梢酝ㄟ^添加緩存層、把靜態(tài)資源(JavaScript、CSS、圖片、視頻文件)轉(zhuǎn)移到內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)上來實(shí)現(xiàn)加速。
6、緩存
緩存是臨時(shí)的存儲(chǔ)空間,用于存儲(chǔ)一些很耗時(shí)的響應(yīng)結(jié)果或者內(nèi)存中經(jīng)常被訪問的數(shù)據(jù),這樣后續(xù)再訪問這些數(shù)據(jù)時(shí)能更快。如圖1-6所示,每次加載一個(gè)新網(wǎng)頁,都要執(zhí)行一個(gè)或者多個(gè)數(shù)據(jù)庫請(qǐng)求來獲取數(shù)據(jù)。不斷向數(shù)據(jù)庫發(fā)送請(qǐng)求會(huì)使應(yīng)用的性能受到很大影響,而緩存可以緩解這種情況。
緩存層
緩存層是一個(gè)臨時(shí)數(shù)據(jù)存儲(chǔ)層,比數(shù)據(jù)庫快很多。設(shè)置獨(dú)立緩存層的好處有:提高系統(tǒng)性能,減輕數(shù)據(jù)庫的工作負(fù)載以及能夠單獨(dú)擴(kuò)展緩存層。圖1-7展示了一種設(shè)置緩存層的方式。
圖片
當(dāng)收到一個(gè)請(qǐng)求時(shí),Web服務(wù)器首先檢查緩存中是否有可用的數(shù)據(jù):如果有,Web服務(wù)器就直接將數(shù)據(jù)返回給客戶端;如果沒有,就去查詢數(shù)據(jù)庫并把返回的響應(yīng)存儲(chǔ)在緩存中,再將其返回給Web服務(wù)器。這種緩存策略叫作通過緩存讀(Read-through Cache)。根據(jù)數(shù)據(jù)的類型、大小和訪問模式,可以采用不同的緩存策略。在網(wǎng)站Codeahoy上有一篇文章“Caching Strategies and How to Choose the Right One”,解釋了不同的緩存策略是如何工作的。
大部分緩存服務(wù)器都為常見的編程語言提供了API,與其進(jìn)行交互很簡單。下面的代碼段展示了典型的Memcached API:
SECONDS=1 cache.set('myKey','hithere',3600*SECONDS) cache.get('myKey')
使用緩存時(shí)的注意事項(xiàng)
使用緩存時(shí)有以下幾點(diǎn)需要注意:
—決定什么時(shí)候應(yīng)使用緩存。如果對(duì)數(shù)據(jù)的讀操作很頻繁,而修改卻不頻繁,則可考慮使用緩存。因?yàn)楸痪彺娴臄?shù)據(jù)是存儲(chǔ)在易變的內(nèi)存中的,所以緩存服務(wù)器不是持久化數(shù)據(jù)的理想位置。比如,如果緩存服務(wù)器重啟,其中的所有數(shù)據(jù)就會(huì)丟失。因此,重要的數(shù)據(jù)應(yīng)該保存在持久性的數(shù)據(jù)存儲(chǔ)中。
—過期策略。執(zhí)行過期策略是好的做法。一旦緩存中的數(shù)據(jù)過期,就應(yīng)該將其從緩存中清除。如果不設(shè)置過期策略,緩存中的數(shù)據(jù)會(huì)一直被保存在內(nèi)存中。通常建議不要把過期時(shí)間設(shè)得太短,因?yàn)檫@樣會(huì)導(dǎo)致系統(tǒng)不得不經(jīng)常從數(shù)據(jù)庫重新加載數(shù)據(jù);當(dāng)然,也不要設(shè)得太長,這樣會(huì)導(dǎo)致數(shù)據(jù)過時(shí)。
—一致性:這關(guān)系到數(shù)據(jù)存儲(chǔ)和緩存的同步。當(dāng)對(duì)數(shù)據(jù)的修改在數(shù)據(jù)存儲(chǔ)和緩存中不是通過同一個(gè)事務(wù)來操作的時(shí)候,就會(huì)發(fā)生不一致。當(dāng)跨越多個(gè)地區(qū)進(jìn)行擴(kuò)展時(shí),保持?jǐn)?shù)據(jù)存儲(chǔ)和緩存之間的一致性是很有挑戰(zhàn)性的。如果你感興趣,可以閱讀Facebook的文章“Scaling Memcache at Facebook”。
—減輕出錯(cuò)的影響:單緩存服務(wù)器是系統(tǒng)中的一個(gè)潛在單點(diǎn)故障(Single Point Of Failure,SPOF)(如圖1-8所示)。在維基百科中,單點(diǎn)故障的定義如下:“單點(diǎn)故障是指系統(tǒng)中的某一部分,如果它出現(xiàn)故障,整個(gè)系統(tǒng)就不能工作”。所以,推薦的做法是在不同的數(shù)據(jù)中心部署多個(gè)緩存服務(wù)器以避免單點(diǎn)故障。另一個(gè)推薦的做法是為緩存超量提供一定比例的內(nèi)存,這樣可以在內(nèi)存使用量上升時(shí)提供一定的緩沖。
驅(qū)逐策略:一旦緩存已滿,任何對(duì)緩存添加條目的請(qǐng)求都有可能導(dǎo)致已有條目被刪除,這叫作緩存驅(qū)逐。LRU(Least-Recently-Used,最近最少使用)是最流行的緩存驅(qū)逐策略。也可以采用其他緩存驅(qū)逐策略,比如LFU(Least Frequently Used,最不經(jīng)常使用)或者FIFO(First In First Out,先進(jìn)先出),以滿足不同的使用場景。
圖片
7、單服務(wù)器配置
內(nèi)容分發(fā)網(wǎng)絡(luò)(Content Delivery Network,CDN)是由在地理上分散的服務(wù)器組成的網(wǎng)絡(luò),被用來傳輸靜態(tài)內(nèi)容。CDN中的服務(wù)器緩存了像圖片、視頻、CSS和JavaScript文件這一類的靜態(tài)內(nèi)容。
動(dòng)態(tài)內(nèi)容緩存是一個(gè)相對(duì)新的概念,不在本書討論的范圍內(nèi)。它可以基于請(qǐng)求路徑、查詢字符串、cookie和請(qǐng)求頭來緩存HTML頁面。感興趣的讀者可以訪問ASW的網(wǎng)站以了解更多內(nèi)容。本書只講解如何使用CDN緩存靜態(tài)內(nèi)容。
現(xiàn)在我們大致介紹一下CDN是如何工作的:當(dāng)用戶訪問一個(gè)網(wǎng)站時(shí),離用戶最近的CDN服務(wù)器會(huì)返回靜態(tài)資源。給人的直觀感受是,離CDN服務(wù)器越遠(yuǎn),網(wǎng)站加載內(nèi)容就越慢。舉個(gè)例子,如果CDN服務(wù)器在舊金山,那么洛杉磯的用戶就比歐洲的用戶更快獲取網(wǎng)站內(nèi)容。圖1-9展示了CDN是如何縮短加載時(shí)間的。
圖片
圖1-10展示了CDN的工作流。
圖片
1.用戶A嘗試通過請(qǐng)求圖片的URL去獲取image.png。這個(gè)URL的域名由CDN服務(wù)商提供。亞馬遜和Akamai CDN上的圖片URL大概是下面這個(gè)樣子:
—https://mysite.cloudfront.net/logo.jpg
—https://mysite.akamai.com/image-manager/img/logo.jpg
2.如果CDN服務(wù)器的緩存中沒有image.png,CDN服務(wù)器就會(huì)向數(shù)據(jù)源服務(wù)器請(qǐng)求這個(gè)文件。數(shù)據(jù)源服務(wù)器可以是Web服務(wù)器,或者線上存儲(chǔ),比如Amazon S3。
3.?dāng)?shù)據(jù)源服務(wù)器將image.png文件返回給CDN服務(wù)器,其中包括可選的HTTP頭Time-to-Live(TTL,生存時(shí)間)。TTL描述了該圖片文件應(yīng)該被緩存多長時(shí)間。
4.CDN服務(wù)器緩存這個(gè)圖片并將其返回給用戶A。這個(gè)圖片一直緩存在CDN服務(wù)器中,直到TTL到期。
5.用戶B發(fā)送請(qǐng)求,要求獲取這張圖片。
6.只要TTL還沒到期,CDN服務(wù)器的緩存就會(huì)返回該圖片。
使用CDN時(shí)的注意事項(xiàng)
—花銷:CDN是由第三方供應(yīng)商來運(yùn)營的,對(duì)數(shù)據(jù)在CDN中的進(jìn)出都會(huì)收費(fèi)。緩存不經(jīng)常使用的內(nèi)容,并不能給性能帶來顯著的好處,應(yīng)該考慮把這些內(nèi)容從CDN中移出。
—設(shè)置合理的緩存過期時(shí)間:對(duì)于時(shí)間敏感的內(nèi)容,設(shè)置緩存過期時(shí)間是很重要的。這個(gè)時(shí)間不應(yīng)該過長或過短。如果過長,內(nèi)容會(huì)不夠新。如果過短,可能導(dǎo)致頻繁地將內(nèi)容從數(shù)據(jù)源服務(wù)器重新加載至CDN。
—CDN回退:要好好考慮你的網(wǎng)站或應(yīng)用如何應(yīng)對(duì)CDN故障。如果CDN出現(xiàn)故障暫時(shí)無法提供服務(wù),客戶端應(yīng)該有能力發(fā)現(xiàn)這個(gè)問題,并直接向數(shù)據(jù)源服務(wù)器請(qǐng)求資源。
—作廢文件:以下操作均可以在文件過期之前將其從CDN中移除。
調(diào)用CDN服務(wù)商提供的API來作廢CDN對(duì)象。
通過對(duì)象版本化來提供一個(gè)不同版本的對(duì)象??梢栽赨RL中添加一個(gè)參數(shù),比如版本號(hào),來給一個(gè)對(duì)象添加版本。比如,在查詢字符串中可以加入版本號(hào)2(image.png?v=2)。
圖1-11展示了加入了CDN和緩存之后的系統(tǒng)設(shè)計(jì)方案。
1.靜態(tài)資源(JavaScript代碼、CSS文件、圖片等)不再由Web服務(wù)器提供,而是從CDN中獲取,以提高響應(yīng)速度。
2.?dāng)?shù)據(jù)被緩存后,數(shù)據(jù)庫的負(fù)載就減輕了。
8、無狀態(tài)網(wǎng)絡(luò)層
現(xiàn)在是時(shí)候考慮橫向擴(kuò)展網(wǎng)絡(luò)層了。為此,我們需要將狀態(tài)(例如,用戶會(huì)話數(shù)據(jù))從網(wǎng)絡(luò)層中移出。一個(gè)好的做法是將會(huì)話數(shù)據(jù)存儲(chǔ)在持久性存儲(chǔ)(如關(guān)系型數(shù)據(jù)庫或NoSQL)中。集群中的每個(gè)Web服務(wù)器都可以經(jīng)由數(shù)據(jù)庫訪問狀態(tài)數(shù)據(jù)。這就是所謂的無狀態(tài)網(wǎng)絡(luò)層。
有狀態(tài)架構(gòu)
有狀態(tài)的和無狀態(tài)的服務(wù)器是有一些關(guān)鍵差異的。有狀態(tài)的服務(wù)器處理客戶端發(fā)來的一個(gè)個(gè)請(qǐng)求,并記下客戶端的數(shù)據(jù)(狀態(tài))。無狀態(tài)的服務(wù)器則不保存狀態(tài)信息。
圖1-12展示了一個(gè)有狀態(tài)架構(gòu)。
圖片
在圖1-12所示的架構(gòu)中,用戶A的會(huì)話數(shù)據(jù)和個(gè)人資料圖片會(huì)被存儲(chǔ)到服務(wù)器1上。為了對(duì)用戶A進(jìn)行身份驗(yàn)證,必須將HTTP請(qǐng)求發(fā)給服務(wù)器1。如果將請(qǐng)求發(fā)給其他服務(wù)器,比如服務(wù)器2,由于服務(wù)器2上沒有用戶A的會(huì)話數(shù)據(jù),因此身份驗(yàn)證就會(huì)失敗。同理,用戶B的所有HTTP請(qǐng)求必須發(fā)給服務(wù)器2;用戶C的所有請(qǐng)求必須發(fā)給服務(wù)器3。
現(xiàn)在的問題是,如何將來自同一客戶端的所有請(qǐng)求都發(fā)給同一個(gè)服務(wù)器。大部分負(fù)載均衡器都提供的黏性會(huì)話可以解決這個(gè)問題,但是會(huì)增加成本。這種方法使得添加或者移除服務(wù)器變得更加困難,同時(shí)也使得應(yīng)對(duì)服務(wù)器故障變得更具挑戰(zhàn)性。
無狀態(tài)架構(gòu)
圖1-13展示了一個(gè)無狀態(tài)架構(gòu)。
在這個(gè)無狀態(tài)架構(gòu)中,用戶的HTTP請(qǐng)求可以發(fā)給任意Web服務(wù)器,然后Web服務(wù)器從共享的數(shù)據(jù)存儲(chǔ)中拉取數(shù)據(jù)。狀態(tài)數(shù)據(jù)存儲(chǔ)在共享數(shù)據(jù)存儲(chǔ)而非Web服務(wù)器中。無狀態(tài)的系統(tǒng)更加簡單,更健壯,也更容易擴(kuò)展。
圖1-14展示了加入了無狀態(tài)網(wǎng)絡(luò)層后的系統(tǒng)設(shè)計(jì)。
圖片
圖片
如圖1-14所示,我們把會(huì)話數(shù)據(jù)從網(wǎng)絡(luò)層中移出,放到持久化存儲(chǔ)中保存。共享數(shù)據(jù)存儲(chǔ)可以是關(guān)系型數(shù)據(jù)庫或者NoSQL(比如,Memcached、Redis)。選擇NoSQL的原因是它容易擴(kuò)展。自動(dòng)擴(kuò)展的意思是,基于網(wǎng)絡(luò)流量自動(dòng)地增加或者減少Web服務(wù)器。將狀態(tài)數(shù)據(jù)從Web服務(wù)器中移除后,就很容易實(shí)現(xiàn)網(wǎng)絡(luò)層的自動(dòng)擴(kuò)展了。
如果你的網(wǎng)站發(fā)展迅速,而且吸引了非常多的國際用戶,要提高可用性以及在更廣的地理區(qū)域提供更好的用戶體驗(yàn),讓網(wǎng)站支持多數(shù)據(jù)中心就非常關(guān)鍵。
9、數(shù)據(jù)中心
圖1-15展示了有兩個(gè)數(shù)據(jù)中心的例子。正常情況下,用戶會(huì)被基于地理位置的域名服務(wù)導(dǎo)流到最近的數(shù)據(jù)中心,也就是說流量被分散到不同的數(shù)據(jù)中心,在圖1-15中有美國東部和美國西部兩個(gè)數(shù)據(jù)中心。基于地理位置的域名服務(wù)(geoDNS)是一種基于用戶的地理位置將域名解析為不同IP地址的DNS服務(wù)。
圖片
如果有某個(gè)數(shù)據(jù)中心出現(xiàn)嚴(yán)重的故障,可以把所有的流量轉(zhuǎn)到另一個(gè)運(yùn)轉(zhuǎn)正常的數(shù)據(jù)中心。在圖1-16所示的例子中,數(shù)據(jù)中心2(美國西部)發(fā)生了故障,全部流量被轉(zhuǎn)至數(shù)據(jù)中心1(美國東部)。
圖片
要設(shè)置多數(shù)據(jù)中心,必須先解決如下技術(shù)難題:
—流量重定向。要有能把流量引導(dǎo)到正確數(shù)據(jù)中心的有效工具。geoDNS可以基于用戶的地理位置把流量引導(dǎo)到最近的數(shù)據(jù)中心。
—數(shù)據(jù)同步。不同地區(qū)的用戶可以使用不同的本地?cái)?shù)據(jù)庫或者緩存。在故障轉(zhuǎn)移的場景中,流量可能被轉(zhuǎn)到一個(gè)數(shù)據(jù)不可用的數(shù)據(jù)中心。常用的一個(gè)策略是在多個(gè)數(shù)據(jù)中心復(fù)制數(shù)據(jù)。Netflix工程博客上的文章“Active-Active for Multi-Regional Resiliency”說明了Netflix是如何實(shí)現(xiàn)多數(shù)據(jù)中心異步復(fù)制的。
—測試和部署:設(shè)置多數(shù)據(jù)中心后,在不同的地點(diǎn)測試你的網(wǎng)站/應(yīng)用是很重要的。而自動(dòng)部署工具則對(duì)于確保所有數(shù)據(jù)中心的服務(wù)一致性至關(guān)重要。
為了進(jìn)一步擴(kuò)展我們的系統(tǒng),我們需要解耦系統(tǒng)中不同的組件,這樣它們就可以單獨(dú)擴(kuò)展了。在現(xiàn)實(shí)世界中,很多分布式系統(tǒng)用消息隊(duì)列來解決這個(gè)問題。
10、消息列隊(duì)
消息隊(duì)列是一個(gè)持久化的組件,存儲(chǔ)在內(nèi)存中,支持異步通信。它被用作緩沖區(qū),分配異步的請(qǐng)求。消息隊(duì)列的基本架構(gòu)很簡單:輸入服務(wù)(也稱為生產(chǎn)者或發(fā)布者)創(chuàng)建消息,并把它們發(fā)布到消息隊(duì)列中;其他服務(wù)或者服務(wù)器(也稱為消費(fèi)者或訂閱者)與消息隊(duì)列連接,并執(zhí)行消息所定義的操作。這個(gè)模型如圖1-17所示。
圖片
解耦使消息隊(duì)列成為構(gòu)建可擴(kuò)展和可靠應(yīng)用的首選架構(gòu)。有了消息隊(duì)列,當(dāng)消費(fèi)者無法處理消息時(shí),生產(chǎn)者依然可以將消息發(fā)布到隊(duì)列中;就算生產(chǎn)者不可用,消費(fèi)者也可以從隊(duì)列中讀取消息。
考慮以下用例:你的應(yīng)用支持修改圖像,包括裁剪、銳化、模糊化等,這些任務(wù)都需要時(shí)間來完成。在圖1-18中,Web服務(wù)器把圖像處理的任務(wù)發(fā)布到消息隊(duì)列。圖像處理進(jìn)程或服務(wù)(Worker)從消息隊(duì)列中領(lǐng)取這個(gè)任務(wù),并異步執(zhí)行。生產(chǎn)者和消費(fèi)者都可以獨(dú)立地?cái)U(kuò)展。隊(duì)列的規(guī)模變大以后,可以加入更多的Worker,以減少處理時(shí)間。如果隊(duì)列在大部分時(shí)間中都是空的,就可以減少Worker的數(shù)量。
11、錄日志、收集指標(biāo)與自動(dòng)化
對(duì)于一個(gè)只有幾臺(tái)服務(wù)器的小網(wǎng)站,記錄日志、收集指標(biāo)和自動(dòng)化只是錦上添花的實(shí)踐而非必需的工作。但是當(dāng)網(wǎng)站發(fā)展成為大企業(yè)提供服務(wù)的平臺(tái)時(shí),這些工作就是必需的了。
記錄日志:監(jiān)控錯(cuò)誤日志非常重要,因?yàn)樗梢詭椭R(shí)別系統(tǒng)的錯(cuò)誤和問題。你可以監(jiān)控每個(gè)服務(wù)器的錯(cuò)誤日志,也可以用工具把各個(gè)服務(wù)器的日志匯總到一個(gè)中心化的服務(wù)中,方便搜索和查看。
收集指標(biāo):收集不同類型的指標(biāo)數(shù)據(jù),有助于獲得商業(yè)洞察力和了解系統(tǒng)的健康狀態(tài)。
以下幾個(gè)指標(biāo)很有用:
—主機(jī)級(jí)別指標(biāo):CPU、內(nèi)存、磁盤I/O等。
—聚合級(jí)別指標(biāo):比如整個(gè)數(shù)據(jù)庫層的性能,整個(gè)緩存層的性能等。
—關(guān)鍵業(yè)務(wù)指標(biāo):每日活躍用戶數(shù)、留存率、收益等。
自動(dòng)化:當(dāng)系統(tǒng)變得龐大且復(fù)雜時(shí),就需要?jiǎng)?chuàng)建或者使用自動(dòng)化工具來提高生產(chǎn)力。持續(xù)集成是一個(gè)很好的做法。在這種做法中,每次代碼檢入(check in)都需要通過自動(dòng)化工具的審核,使團(tuán)隊(duì)能及時(shí)發(fā)現(xiàn)問題。同時(shí),將構(gòu)建、測試和部署等流程自動(dòng)化,可以顯著提高開發(fā)人員的生產(chǎn)力。
添加消息隊(duì)列和各種工具
圖1-19展示了更新后的系統(tǒng)設(shè)計(jì),因?yàn)閳D書版面有限,只畫了一個(gè)數(shù)據(jù)中心。
1.這個(gè)系統(tǒng)中包含一個(gè)消息隊(duì)列,它使系統(tǒng)更加松散地耦合且更容易從故障中恢復(fù)。
2.它包含了記錄日志、監(jiān)控和收集指標(biāo)的功能,以及自動(dòng)化工具。
隨著數(shù)據(jù)與日俱增,你的數(shù)據(jù)庫過載變得越來越嚴(yán)重。是時(shí)候擴(kuò)展數(shù)據(jù)層了。
12、數(shù)據(jù)庫擴(kuò)展
數(shù)據(jù)庫的擴(kuò)展有兩種方式:縱向擴(kuò)展和橫向擴(kuò)展。
縱向擴(kuò)展
縱向擴(kuò)展又叫作向上擴(kuò)展,就是為已有機(jī)器增加算力(CPU、內(nèi)存、硬盤等)。業(yè)界有一些非常強(qiáng)勁的數(shù)據(jù)庫服務(wù)器。亞馬遜的RDS(關(guān)系型數(shù)據(jù)庫服務(wù))可以提供擁有24 TB內(nèi)存的數(shù)據(jù)庫服務(wù)器。這種性能強(qiáng)勁的數(shù)據(jù)庫服務(wù)器可以存儲(chǔ)和處理非常多的數(shù)據(jù)。舉個(gè)例子,Stack Overflow的網(wǎng)站在2013年每個(gè)月有超過1000萬的獨(dú)立用戶訪問,但是它只有一個(gè)主數(shù)據(jù)庫。然而,縱向擴(kuò)展也有一些重大缺點(diǎn):
—盡管可以給數(shù)據(jù)庫服務(wù)器添加更多的CPU、內(nèi)存等,但是硬件的能力總是有上限的。如果網(wǎng)站的用戶基數(shù)很大,單服務(wù)器是不夠的。
—更大的單點(diǎn)故障風(fēng)險(xiǎn)。
—總成本很高。強(qiáng)勁的服務(wù)器比一般的服務(wù)器貴很多。
橫向擴(kuò)展
橫向擴(kuò)展,也叫分片,就是添加更多服務(wù)器。圖1-20對(duì)比了縱向擴(kuò)展和橫向擴(kuò)展。
數(shù)據(jù)庫分片是指把大數(shù)據(jù)庫拆分成更小、更容易管理的部分(這些部分叫作Shard,分片)。每個(gè)Shard共享同樣的數(shù)據(jù)庫Schema,但是里面的數(shù)據(jù)都是這個(gè)Shard獨(dú)有的。
圖1-21展示了一個(gè)做了分片的數(shù)據(jù)庫。根據(jù)用戶ID,用戶數(shù)據(jù)被分配到其中一個(gè)數(shù)據(jù)庫服務(wù)器上。每次要訪問數(shù)據(jù)時(shí),就會(huì)用一個(gè)哈希函數(shù)來找對(duì)應(yīng)的Shard。在我們的例子中,以u(píng)ser_id(用戶ID)對(duì)4求余作為哈希函數(shù)。如果余數(shù)為0,那么Shard 0就被用來存儲(chǔ)和獲取數(shù)據(jù);如果余數(shù)為1,就用Shard 1,依此類推。
圖片
圖1-22展示了做過分片的數(shù)據(jù)庫中的用戶表示例。
實(shí)施分片策略時(shí),要考慮的最重要的問題是選擇什么分片鍵(Sharding Key)。分片鍵(也叫作分區(qū)鍵,Partition Key)由一個(gè)或者多個(gè)數(shù)據(jù)列組成,用來決定將數(shù)據(jù)分到哪個(gè)Shard。在圖1-22所示的例子中,user_id被用作分片鍵。分片鍵可以把數(shù)據(jù)庫查詢路由到正確的數(shù)據(jù)庫,使你高效地檢索和修改數(shù)據(jù)。在選擇分片鍵時(shí),最重要的標(biāo)準(zhǔn)之一是選擇一個(gè)可以讓數(shù)據(jù)均勻分布的鍵。
分片是一種不錯(cuò)的擴(kuò)展數(shù)據(jù)庫的技術(shù),但它還遠(yuǎn)不是一個(gè)完美的解決方案。它為系統(tǒng)引入了復(fù)雜性和新的挑戰(zhàn)。
重分片數(shù)據(jù):出現(xiàn)如下情況時(shí),需要對(duì)數(shù)據(jù)重新分片。第一種是因?yàn)閿?shù)據(jù)快速增長,單個(gè)Shard無法存儲(chǔ)更多的數(shù)據(jù)。第二種是因?yàn)閿?shù)據(jù)的分布不均勻,有些Shard的空間可能比其他的更快耗盡。當(dāng)Shard被耗盡時(shí),就需要更新用于分片的哈希函數(shù),然后把數(shù)據(jù)移到別的地方去。我們會(huì)在第5章介紹一致性哈希算法,它是解決這個(gè)問題的常用技術(shù)。
名人問題:也叫作熱點(diǎn)鍵問題。過多訪問一個(gè)特定的Shard可能造成服務(wù)器過載。想象一下,把Katy Perry、Justin Bieber和Lady Gaga的數(shù)據(jù)都放在同一個(gè)Shard里,對(duì)于社交應(yīng)用而言,這個(gè)Shard會(huì)因讀操作太多而不堪重負(fù)。為了解決這個(gè)問題,我們可能需要為每個(gè)名人都分配一個(gè)Shard,而且每個(gè)Shard可能還需要進(jìn)一步分區(qū)。
連接和去規(guī)范化(de-normalization):一旦數(shù)據(jù)庫通過分片被劃分到多個(gè)服務(wù)器上,就很難跨數(shù)據(jù)庫分片執(zhí)行連接(join)操作了。解決這個(gè)問題的常用方法就是對(duì)數(shù)據(jù)庫去規(guī)范化,把數(shù)據(jù)冗余存儲(chǔ)到多張表中,以便查詢可以在一張表中執(zhí)行。
在圖1-23中,我們對(duì)數(shù)據(jù)庫做了分片,以支持?jǐn)?shù)據(jù)流量的快速增長;同時(shí),將有些非關(guān)系型功能遷移到NoSQL數(shù)據(jù)庫中,以降低數(shù)據(jù)庫的負(fù)載。High Scalability網(wǎng)站上有一篇文章“What the Heck are You Actually Using NoSQL for?”介紹了很多NoSQL數(shù)據(jù)庫的使用案例。
圖片
審核編輯:湯梓紅
-
Web
+關(guān)注
關(guān)注
2文章
1263瀏覽量
69476 -
服務(wù)器
+關(guān)注
關(guān)注
12文章
9160瀏覽量
85425 -
數(shù)據(jù)庫
+關(guān)注
關(guān)注
7文章
3799瀏覽量
64395
原文標(biāo)題:系統(tǒng)設(shè)計(jì),被我拿捏了!
文章出處:【微信號(hào):小林coding,微信公眾號(hào):小林coding】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論