Web分布式系統(tǒng)設計的原則。開源軟件已經(jīng)成為許多大型網(wǎng)站的基本組成部分,隨著這些網(wǎng)站的逐步壯大,他們的網(wǎng)站架構和一些指導原則也出現(xiàn)在開發(fā)者們的面前,給予切實有用的指導和幫助。本文旨在介紹一些核心問題以及通過構建模塊來制作大型網(wǎng)站,實現(xiàn)終目標。
構建并運營一個可伸縮的Web站點或應用程序到底指的是什么?在初,僅是通過互聯(lián)網(wǎng)連接用戶和訪問遠程資源。
和大多數(shù)事情一樣,當構建一個Web服務時,需要提前抽出時間進行規(guī)劃。了解大型網(wǎng)站創(chuàng)建背后的注意事項以及權衡可能會給你帶來更加明智的決策,當你在創(chuàng)建小網(wǎng)站時。下面是設計大型Web系統(tǒng)時,需要注意的一些核心原則:
1.可用性
2.性能
3.可靠性
4.可擴展
5.易管理
6.成本
上面的這些原則給設計分布式Web架構提供了一定的基礎和理論指導。然而,它們也可能彼此相左,例如實現(xiàn)這個目標的代價是犧牲成本。一個簡單的例子:選擇地址容量,僅通過添加更多的服務器(可伸縮性),這個可能以易管理(你不得不操作額外的服務器)和成本作為代價(服務器價格)。
無論你想設計哪種類型的Web應用程序,這些原則都是非常重要的,甚至這些原則之間也會互相羈絆,做好它們之間的權衡也非常重要。
一、基礎
當涉及到系統(tǒng)架構問題時,這幾件事情是必須要考慮清楚的:什么樣的模塊比較合適?如何把它們組合在一起?如何進行恰當?shù)貦嗪??在擴大投資之前,它通常需要的并不是一個精明的商業(yè)命題,然而,一些深謀遠慮的設計可以幫你在未來節(jié)省大量的時間和資源。
討論的重點幾乎是構建所有大型Web應用程序的核心:服務、冗余、分區(qū)和故障處理能力。這里的每個因素都會涉及到選擇和妥協(xié),特別是前面所討論的那些原則。解釋這些核心的佳辦法就是舉例子。
圖片托管應用程序
有時,你會在線上傳圖片,而一些大型網(wǎng)站需要托管和傳送大量的圖片,這對于構建一個具有成本效益、高可用性并具有低延時(快速檢索)的架構是一項挑戰(zhàn)。
在一個圖片系統(tǒng)中,用戶可以上傳圖片到一個中央服務器里,通過網(wǎng)絡連接或API對這些圖片進行請求,就像Flickr或者Picasa。簡單點,我們就假設這個應用程序只包含兩個核心部分:上傳(寫)圖片和檢索圖片。圖片上傳時好能夠做到高效,傳輸速度也是我們關心的,當有人向圖片發(fā)出請求時(例如是一個Web頁面或其他應用程序)。這是非常相似的功能,提供Web服務或內容分發(fā)網(wǎng)絡(一個CDN服務器可以在許多地方存儲內容,所以無論是在地理上還是物理上都更加接近用戶,從而導致更快的性能)邊緣服務器。
該系統(tǒng)需要考慮的其他重要方面:
1.圖片存儲的數(shù)量是沒有限制的,所以存儲應具備可伸縮,另外圖片計算也需要考慮
2.下載/請求需要做到低延遲
3.用戶上傳一張圖片,那么圖片就應該始終在那里(圖片數(shù)據(jù)的可靠性)
4.系統(tǒng)應該易于維護(易管理)
5.由于圖片托管不會有太高的利潤空間,所以系統(tǒng)需要具備成本效益
二、 服務
當我們考慮構建可伸縮的系統(tǒng)時,它應有助于解耦功能,系統(tǒng)的每個部分都可以作為自己的服務并且擁有清晰的接口定義。在實踐中,這種系統(tǒng)設計被稱作面向服務的體系結構(SOA)。對于此類系統(tǒng),每個服務都有它自己的獨特功能,通過一個抽象接口可以與外面的任何內容進行互動,通常是面向公眾的另一個服務API。
把系統(tǒng)分解成一組互補性的服務,在互相解耦這些操作塊。這種抽象有助于在服務、基本環(huán)境和消費者服務之間建立非常清晰的關系。這種分解可以有效地隔離問題,每個塊也可以互相伸縮。這種面向服務的系統(tǒng)設計與面向對象設計非常相似。
快進并假設服務正在大量使用;在這種情況下,很容易看到寫圖片的時間對讀圖片時間有多大影響(他們兩個功能在彼此競爭共享資源)。根據(jù)各自體系,這種影響會是巨大的。即使上傳和下載速度相同(這是不可能的,對于大多數(shù)的IP網(wǎng)絡來說,下載速度:上傳速度至少是3:1),通常,文件可以從緩存中讀取,而寫入,終是寫到磁盤中(也許在終一致的情況下,可以被多寫幾次)。即使是從緩存或者磁盤(類似SSD)中讀取,數(shù)據(jù)寫入都會比讀慢(Pole Position,一個開源DB基準的開源工具和結果)。
這種設計的另一個潛在問題是像Apache或者Lighttpd這些Web服務器通常都會有一個并發(fā)連接數(shù)上限(默認是500,但也可以更多),這可能會花費高流量,寫可能會迅速消掉所有。既然讀可以異步或利用其他性能優(yōu)化,比如gzip壓縮或分塊傳輸代碼,Web服務可以快速切換讀取和客戶端來服務于更多的請求,超過每秒的大連接數(shù)(Apache的大連接數(shù)設置為500,這種情況并不常見,每秒可以服務幾千個讀取請求)。另一方面,寫通常傾向于保持一個開放的鏈接進行持續(xù)上傳,所以,使用家庭網(wǎng)絡上傳一個1 MB的文件花費的時間可能會超過1秒,所以,這樣的服務器只能同時滿足500個寫請求。
當然,如果你有兩個不同的端點,上面的例子可能會運行的很好(事實上,這非常類似于幾個云存儲供應商之間的實現(xiàn)和內容分發(fā)網(wǎng)絡)。雖然有很多種方法可以解決這些瓶頸,但每個人都會有不同的權衡,所以采用適合你的方法才是重要的。
當談到這些系統(tǒng)時,其實并沒有非常正確的答案,但有助于我們回到文章開始處的原則上看問題。確定系統(tǒng)需求(大量的讀或寫或者兩個都進行、級別并發(fā)、跨數(shù)據(jù)查詢、范圍、種類等等),選擇不同的基準、理解系統(tǒng)是如何出錯的并且對以后的故障發(fā)生情況做些扎實的計劃。
三、冗余
為了可以正確處理錯誤,一個Web架構的服務和數(shù)據(jù)必須具備適當?shù)娜哂?。例如,如果只有一個副本文件存儲在這臺單獨的服務器上,那么如果這臺服務器出現(xiàn)問題或丟失,那么該文件也隨即一起丟失。丟失數(shù)據(jù)并不是什么好事情,避免數(shù)據(jù)丟失的常用方法就是多創(chuàng)建幾個文件或副本或冗余。
同樣也適用于服務器。如果一個應用程序有個核心功能,應確保有多個副本或版本在同時運行,這樣可以避免單節(jié)點失敗。
在系統(tǒng)中創(chuàng)建冗余,當系統(tǒng)發(fā)生危機時,如果需要,可以消除單點故障并提供備份或備用功能。例如,這里有兩個相同的服務示例在生產(chǎn)環(huán)境中運行,如果其中一個發(fā)生故障或者降低,那么該系統(tǒng)容錯轉移至那個健康的副本上。容錯轉移可以自動發(fā)生也可以手動干預。
服務冗余的另一重要組成部分是創(chuàng)建一個無共享架構。在這種體系結構中,每個節(jié)點都能相互獨立運行,并且沒有所謂的中央“大腦”管理狀態(tài)或協(xié)調活動其他節(jié)點。這對系統(tǒng)的可擴展幫助很大,因為新節(jié)點在沒有特殊要求或知識的前提下被添加。然而,重要的是,這些系統(tǒng)是沒有單點故障的,所以失敗的彈性就更大。
例如在我們的圖片服務器應用程序中,所有的圖片在另一個硬件上都有冗余副本(理想情況下是在不同的地理位置,避免在數(shù)據(jù)中心發(fā)生一些火災、地震等自然事故),服務去訪問圖片將被冗余,所有潛在的服務請求。
四、分區(qū)
數(shù)據(jù)集有可能非常大,無法安裝在一臺服務器上。也有可能這樣,某操作需要太多的計算資源、性能降低并且有必要增加容量。在這兩種情況下,你有兩種選擇:縱向擴展或橫向擴展。
縱向擴展意味著在單個服務器上添加更多的資源。所以,對于一個非常大的數(shù)據(jù)集來說,這可能意味著添加更多(或更大)的硬件設備,來使一臺服務器能容下整個數(shù)據(jù)集。在計算操作下,這可能意味著移動計算到一個更大的服務器上,擁有更快的CPU或更大的內存。在各種情況下,縱向擴展可以通過提升單個資源的處理能力來完成。
橫向擴展在另一方面是添加更多的節(jié)點,在大數(shù)據(jù)集下,這可能會使用第二服務器來存儲部分數(shù)據(jù)集,對于計算資源來說,這意味著分割操作或跨節(jié)點加載。為了充分利用橫向擴展,它應作為一種內在的系統(tǒng)架構設計原則,否則修改或拆分操作將會非常麻煩。
當談到橫向擴展時,常見的做法是把服務進行分區(qū)或碎片。分區(qū)可以被派發(fā),這樣每個邏輯組的功能就是獨立的。可以通過地理界限或其他標準,如非付費與付費用戶來完成分區(qū)。這些方案的優(yōu)點是他們會隨著容量的增加提供一個服務或數(shù)據(jù)存儲。
在我們的圖片服務器案例中,用來存儲圖片的單個文件服務器可能被多個文件服務器取代,每個里面都會包含一套自己獨特的圖像。這種架構將允許系統(tǒng)來填充每一個文件/圖片服務器,當磁盤填滿時會添加額外的服務器。這樣的設計需要一個命名方案,用來捆綁圖片文件名到其相應的服務器上。圖像名字可以形成一個一致的哈希方案并映射到整個服務器上;或者給每張圖片分配一個增量ID,當客戶端對圖片發(fā)出請求時,圖片檢索服務只需要檢索映射到每個服務器上(例如索引)的ID。
當然,跨越多個服務器對數(shù)據(jù)或功能進行分區(qū)還是有許多挑戰(zhàn)的。其中的關鍵問題是數(shù)據(jù)本地化。在分布式系統(tǒng)中,數(shù)據(jù)操作或計算點越接近,系統(tǒng)性能就會越好。因此,它也可能是個潛在問題,當數(shù)據(jù)分散在多個服務器上時。有時數(shù)據(jù)不是在本地,那么就要迫使服務器通過網(wǎng)絡來獲取所需的信息,這個獲取的過程就會設計到成本。
另一潛在問題是不一致。當這里有多個服務對一個共享資源執(zhí)行讀寫操作時,潛在可能會有另一個服務器或數(shù)據(jù)存儲參與進來,作為競選條件——一些數(shù)據(jù)需要更新,但是讀的優(yōu)先級高于更新——在這種情況下,數(shù)據(jù)就是不一致的。例如在圖片托管方案中,有可能出現(xiàn)的不一致是:如果一個客戶端發(fā)送更新“狗”圖片請求,進行重新命名,把“Dog”改成“Gizmo”,但同時,另一個客戶端正在讀這張圖片。在這種情況下,標題就是不清楚的。“Dog”或“Gizmo”應該被第二個客戶端接收。
當然,在進行數(shù)據(jù)分區(qū)時會產(chǎn)生一些障礙,但是分區(qū)允許把每個問題拆分到管理群里——通過數(shù)據(jù)、負載、使用模式等。這樣對可擴展和易管理都是有幫助的,但也不是沒有風險的。這里有很多方式來降低風險和故障處理;然而,為了簡便起見,并未在本文中詳細說明,如果你有興趣,可以訪問我的博客。
總結
以上介紹的都是設計分布式系統(tǒng)需要考慮的核心要素。可用性、性能、可靠性、可擴展、易管理、成本這幾個原則非常重要,但在實際應用中可能會以犧牲某個原則來實現(xiàn)另外一個原則,在這個過程中就要做好權衡工作,做到因時制宜。