構建高可擴Web架構和分佈式系統實戰(上)(轉載CSDN)

本文作者Kate Matsudaira是一位美麗的女工程副總裁,曾在Sun Microsystems、微軟、亞馬遜這些一流的IT公司任職。她有着非常豐富的工作經驗和團隊管理經驗,當過程序員、項目經理、產品經理以及人事經理。專注於構建和操作大型Web應用程序/網站,目前她的主要研究方向是SaaS(軟件即服務)應用程序和雲計算(如大家所說的大數據)。

本文是作者在AOSA一書介紹如何構建可擴展的分佈式系統裏的內容,在此翻譯並分享給大家。

開源軟件已經成爲許多大型網站的基本組成部分,隨着這些網站的逐步壯大,他們的網站架構和一些指導原則也開放在開發者們的面前,給予大家切實有用的指導和幫助。

這篇文章主要側重於Web系統,並且也適用於其他分佈式系統。

Web分佈式系統設計的原則

構建並運營一個可伸縮的Web站點或應用程序到底是指什麼?在最初,僅是通過互聯網連接用戶和訪問遠程資源。

和大多數事情一樣,當構建一個Web服務時,需要提前抽出時間進行規劃。瞭解大型網站創建背後的注意事項以及學會權衡,會給你帶來更加明智的決策。下面是設計大型Web系統時,需要注意的一些核心原則:

  • 可用性
  • 性能
  • 可靠性
  • 可擴展
  • 易管理
  • 成本

上面的這些原則給設計分佈式Web架構提供了一定的基礎和理論指導。然而,它們也可能彼此相左,例如實現這個目標的代價是犧牲成本。一個簡單的例子:選擇地址容量,僅通過添加更多的服務器(可伸縮性),這個可能以易管理(你不得不操作額外的服務器)和成本作爲代價(服務器價格)。

無論你想設計哪種類型的Web應用程序,這些原則都是非常重要的,甚至這些原則之間也會互相羈絆,做好它們之間的權衡也非常重要。

基礎

當涉及到系統架構問題時,這幾件事情是必須要考慮清楚的:什麼樣的模塊比較合適?如何把它們組合在一起?如何進行恰當地權衡?在擴大投資之前,它通常需要的並不是一個精明的商業命題,然而,一些深謀遠慮的設計可以幫你在未來節省大量的時間和資源。

本文討論的重點幾乎是構建所有大型Web應用程序的核心:服務、冗餘、分區和故障處理能力。這裏的每個因素都會涉及到選擇和妥協,特別是前面所討論的那些原則。解釋這些核心的最佳辦法就是舉例子。

圖片託管應用程序

有時,你會在線上傳圖片,而一些大型網站需要託管和傳送大量的圖片,這對於構建一個具有成本效益、高可用性並具有低延時(快速檢索)的架構是一項挑戰。

在一個圖片系統中,用戶可以上傳圖片到一箇中央服務器裏,通過網絡連接或API對這些圖片進行請求,就像Flickr或者Picasa。簡單點,我們就假設這個應用程序只包含兩個核心部分:上傳(寫)圖片和檢索圖片。圖片上傳時最好能夠做到高效,傳輸速度也是我們最關心的,當有人向圖片發出請求時(例如是一個Web頁面或其他應用程序)。這是非常相似的功能,提供Web服務或內容分發網絡(一個CDN服務器可以在許多地方存儲內容,所以無論是在地理上還是物理上都更加接近用戶,從而導致更快的性能)邊緣服務器。

該系統需要考慮的其他重要方面:

  • 圖片存儲的數量是沒有限制的,所以存儲應具備可伸縮,另外圖片計算也需要考慮
  • 下載/請求需要做到低延遲
  • 用戶上傳一張圖片,那麼圖片就應該始終在那裏(圖片數據的可靠性)
  • 系統應該易於維護(易管理)
  • 由於圖片託管不會有太高的利潤空間,所以系統需要具備成本效益

圖1是個簡化的功能圖

圖1 圖片託管系統的簡化結構圖

在這個例子中,系統必須具備快速、數據存儲必須做到可靠和高度可擴展。構建一個小型的應用程序就微不足道了,一臺服務器即可實現託管。如果這樣,這篇文章就毫無興趣和吸引力了。假設我們要做的應用程序會逐漸成長成Flickr那麼大。

服務

當我們考慮構建可伸縮的系統時,它應有助於解耦功能,系統的每個部分都可以作爲自己的服務並且擁有清晰的接口定義。在實踐中,這種系統設計被稱作面向服務的體系結構(SOA)。對於此類系統,每個服務都有它自己的獨特功能,通過一個抽象接口可以與外面的任何內容進行互動,通常是面向公衆的另一個服務API。

把系統分解成一組互補性的服務,在互相解耦這些操作塊。這種抽象有助於在服務、基本環境和消費者服務之間建立非常清晰的關係。這種分解可以有效地隔離問題,每個塊也可以互相伸縮。這種面向服務的系統設計與面向對象設計非常相似。

在我們的例子中,所有上傳和檢索請求都在同一臺服務器上處理。然而,因爲系統需要具備可伸縮性,所以把這兩個功能打破並集成到自己的服務中是有意義的。

快進並假設服務正在大量使用;在這種情況下,很容易看到寫圖片的時間對讀圖片時間會產生多大影響(他們兩個功能在彼此競爭共享資源)。根據各自體系,這種影響會是巨大的。即使上傳和下載速度相同(這是不可能的,對於大多數的IP網絡來說,下載速度:上傳速度至少是3:1),通常,文件可以從緩存中讀取,而寫入,最終是寫到磁盤中(也許在最終一致的情況下,可以被多寫幾次)。即使是從緩存或者磁盤(類似SSD)中讀取,數據寫入都會比讀慢(Pole Position,一個開源DB基準的開源工具和結果)。

這種設計的另一個潛在問題是像Apache或者Lighttpd這些Web服務器通常都會有一個併發連接數上限(默認是500,但也可以更多),這可能會花費高流量,寫可能會迅速消掉所有。既然讀可以異步或利用其他性能優化,比如gzip壓縮或分塊傳輸代碼,Web服務可以快速切換讀取和客戶端來服務於更多的請求,超過每秒的最大連接數(Apache的最大連接數設置爲500,這種情況並不常見,每秒可以服務幾千個讀取請求)。另一方面,寫通常傾向於保持一個開放的鏈接進行持續上傳,所以,使用家庭網絡上傳一個1 MB的文件花費的時間可能會超過1秒,所以,這樣的服務器只能同時滿足500個寫請求。

圖2:讀取分離

規劃這種瓶頸的一個非常好的做法是把讀和寫進行分離,如圖2所示。這樣我們就可以對它們單獨進行擴展(一直以來讀都比寫多)但也有助於弄明白每個點的意思。這種分離更易於排除故障和解決規模方面問題,如慢讀。

這種方法的優點就是我們能夠彼此獨立解決問題——在同種情況下,無需寫入和檢索操作。這兩種服務仍然利用全球語料庫的圖像,但是他們可以自由地優化性能和服務方法(例如排隊請求或者緩存流行圖片——下面會介紹更多)。從維護和成本角度來看,每一個服務都可以根據需要獨立進行擴展,但如果把它們進行合併或交織在一起,那麼有可能無意中就會對另一個性能產生影響,如上面討論的情景。

當然,如果你有兩個不同的端點,上面的例子可能會運行的很好(事實上,這非常類似於幾個雲存儲供應商之間的實現和內容分發網絡)。雖然有很多種方法可以解決這些瓶頸,但每個人都會有不同的權衡,所以採用適合你的方法纔是最重要的。

例如,Flickr解決這個讀/寫問題是通過分發用戶跨越不同的碎片,每個碎片只能處理一組用戶,但是隨着用戶數的增加,更多的碎片也會相應的添加到羣集裏(請參閱Flickr的擴展介紹)。在第一個例子中,它更容易基於硬件的實際用量進行擴展(在整個系統中的讀/寫數量),而Flickr是基於其用戶羣進行擴展(but forces the assumption of equal usage across users so there can be extra capacity)。而前面的那個例子,任何一箇中斷或者問題都會降低整個系統功能(例如任何人都沒辦法執行寫操作),而Flickr的一箇中斷只會影響到其所在碎片的用戶數。在第一個例子中,它更容易通過整個數據集進行操作——例如,更新寫服務,包括新的元數據或者通過所有的圖片元數據進行搜索——而Flickr架構的每個碎片都需要被更新或搜索(或者需要創建一個搜索服務來收集元數據——事實上,他們就是這樣做的)。

當談到這些系統時,其實並沒有非常正確的答案,但有助於我們回到文章開始處的原則上看問題。確定系統需求(大量的讀或寫或者兩個都進行、級別併發、跨數據查詢、範圍、種類等等),選擇不同的基準、理解系統是如何出錯的並且對以後的故障發生情況做些紮實的計劃。

冗餘

爲了可以正確處理錯誤,一個Web架構的服務和數據必須具備適當的冗餘。例如,如果只有一個副本文件存儲在這臺單獨的服務器上,那麼如果這臺服務器出現問題或丟失,那麼該文件也隨即一起丟失。丟失數據並不是什麼好事情,避免數據丟失的常用方法就是多創建幾個文件或副本或冗餘。

同樣也適用於服務器。如果一個應用程序有個核心功能,應確保有多個副本或版本在同時運行,這樣可以避免單節點失敗。

在系統中創建冗餘,當系統發生危機時,如果需要,可以消除單點故障並提供備份或備用功能。例如,這裏有兩個相同的服務示例在生產環境中運行,如果其中一個發生故障或者降低,那麼該系統容錯轉移至那個健康的副本上。容錯轉移可以自動發生也可以手動干預。

服務冗餘的另一重要組成部分是創建一個無共享架構。在這種體系結構中,每個節點都能相互獨立運行,並且沒有所謂的中央“大腦”管理狀態或協調活動其他節點。這對系統的可擴展幫助很大,因爲新節點在沒有特殊要求或知識的前提下被添加。然而,最重要的是,這些系統是沒有單點故障的,所以失敗的彈性就更大。

例如在我們的圖片服務器應用程序中,所有的圖片在另一個硬件上都有冗餘副本(理想情況下是在不同的地理位置,避免在數據中心發生一些火災、地震等自然事故),服務去訪問圖片將被冗餘,所有潛在的服務請求。(參見圖3:採用負載均衡是實現這點的最好方法,在下面還會介紹更多方法)

圖3 圖片託管應用程序冗餘

分區

數據集有可能非常大,無法安裝在一臺服務器上。也有可能這樣,某操作需要太多的計算資源、性能降低並且有必要增加容量。在這兩種情況下,你有兩種選擇:縱向擴展或橫向擴展。

縱向擴展意味着在單個服務器上添加更多的資源。所以,對於一個非常大的數據集來說,這可能意味着添加更多(或更大)的硬件設備,來使一臺服務器能容下整個數據集。在計算操作下,這可能意味着移動計算到一個更大的服務器上,擁有更快的CPU或更大的內存。在各種情況下,縱向擴展可以通過提升單個資源的處理能力來完成。

橫向擴展在另一方面是添加更多的節點,在大數據集下,這可能會使用第二服務器來存儲部分數據集,對於計算資源來說,這意味着分割操作或跨節點加載。爲了充分利用橫向擴展,它應作爲一種內在的系統架構設計原則,否則修改或拆分操作將會非常麻煩。

當談到橫向擴展時,最常見的做法是把服務進行分區或碎片。分區可以被派發,這樣每個邏輯組的功能就是獨立的。可以通過地理界限或其他標準,如非付費與付費用戶來完成分區。這些方案的優點是他們會隨着容量的增加提供一個服務或數據存儲。

在我們的圖片服務器案例中,用來存儲圖片的單個文件服務器可能被多個文件服務器取代,每個裏面都會包含一套自己獨特的圖像。(見圖4)這種架構將允許系統來填充每一個文件/圖片服務器,當磁盤填滿時會添加額外的服務器。這樣的設計需要一個命名方案,用來捆綁圖片文件名到其相應的服務器上。圖像名字可以形成一個一致的哈希方案並映射到整個服務器上;或者給每張圖片分配一個增量ID,當客戶端對圖片發出請求時,圖片檢索服務只需要檢索映射到每個服務器上(例如索引)的ID。

圖4 圖片託管應用程序冗餘和分區

當然,跨越多個服務器對數據或功能進行分區還是有許多挑戰的。其中的關鍵問題是數據本地化。在分佈式系統中,數據操作或計算點越接近,系統性能就會越好。因此,它也可能是個潛在問題,當數據分散在多個服務器上時。有時數據不是在本地,那麼就要迫使服務器通過網絡來獲取所需的信息,這個獲取的過程就會設計到成本。

另一潛在問題是不一致。當這裏有多個服務對一個共享資源執行讀寫操作時,潛在可能會有另一個服務器或數據存儲參與進來,作爲競選條件——一些數據需要更新,但是讀的優先級高於更新——在這種情況下,數據就是不一致的。例如在圖片託管方案中,有可能出現的不一致是:如果一個客戶端發送更新“狗”圖片請求,進行重新命名,把“Dog”改成“Gizmo”,但同時,另一個客戶端正在讀這張圖片。在這種情況下,標題就是不清楚的。“Dog”或“Gizmo”應該被第二個客戶端接收。

當然,在進行數據分區時會產生一些障礙,但是分區允許把每個問題拆分到管理羣裏——通過數據、負載、使用模式等。這樣對可擴展和易管理都是有幫助的,但也不是沒有風險的。這裏有很多方式來降低風險和故障處理;然而,爲了簡便起見,並未在本文中詳細說明,如果你有興趣,可以訪問我的博客

總結

以上介紹的都是設計分佈式系統需要考慮的核心要素。可用性、性能、可靠性、可擴展、易管理、成本這幾個原則非常重要,但在實際應用中可能會以犧牲某個原則來實現另外一個原則,在這個過程中就要做好權衡工作,做到因時制宜。

在下面的構建分佈式系統實戰中,我們將會深入介紹如何設計可擴展的數據訪問,包括負載均衡、代理、全局緩存、分佈式緩存等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章