首先來看一個網站的原始架構,應用程序、文件、資料庫都在一臺伺服器上,使用最常規的LAMP開發。我們來一層層分析這樣一個原始架構存在的問題。

註:本文只是個overview,細節不做過多展開,會在後續章節中逐一具體講述。

1. 一臺伺服器無法滿足多重需求——服務需分離

應用程序需要處理各種複雜的業務邏輯,需要強大的CPU;文件伺服器要做好存儲就需要更大的硬碟;而資料庫需要快速的磁碟檢索和數據緩存,則需要更快的硬碟和更快的內存。一臺伺服器無法同時保障性能、存儲空間等需求,因此,需要將應用程序、文件、資料庫三者分離,做到各司其職。

舉個上傳圖片伺服器與WEB內容服務分離的例子。

  1. WEB伺服器和圖片伺服器分離,這決定了需要將圖片文件傳遞到遠處的圖片伺服器上。那麼可能是用戶上傳圖片時直接上傳到圖片伺服器上,不中轉;
  2. 在圖片伺服器上建立個專門處理文件上傳的應用,用戶在頁面提交上傳時,檢查如果是圖片,則上傳到圖片伺服器,其他內容傳到WEB伺服器;
  3. 如果擔心圖片伺服器效果降低,可以把圖片伺服器擴充為2臺或者是集羣,然後用負載均衡來解決壓力問題。

2. 網站並發遇到瓶頸——集羣與負載均衡

單一伺服器能處理的請求數有限,無法應對業務高峯。那麼可使用應用伺服器集羣來改善網站的並發處理能力,同時,通過負載均衡調度伺服器,分發請求,實現網站的可伸縮性。

負載均衡調度伺服器如何合理分配任務,以保證所有後端伺服器都將性能充分發揮,從而保持伺服器集羣的整體性能最優呢?我們來看看這個有趣的負載均衡問題。

舉一個HTTP重定向實現負載均衡的例子。

當用戶向伺服器發起請求時,請求首先被集羣調度者截獲;調度者根據某種分配策略(後面鋪開描述),選擇一臺伺服器,並將選中的伺服器的IP地址封裝在HTTP響應消息頭部的Location欄位中,並將響應消息的狀態碼設為302,最後將這個響應消息返回給瀏覽器。

當瀏覽器收到響應消息後,解析Location欄位,並向該URL發起請求,然後指定的伺服器處理該用戶的請求,最後將結果返回給用戶。在使用HTTP重定向來實現伺服器集羣負載均衡的過程中,需要一臺伺服器作為請求調度者。用戶的一項操作需要發起兩次HTTP請求,一次向調度伺服器發送請求,獲取後端伺服器的IP,第二次向後端伺服器發送請求,獲取處理結果。

再來展開看看調度策略。

隨機分配策略。當調度伺服器收到用戶請求後,可以隨機決定使用哪臺後端伺服器,然後將該伺服器的IP封裝在HTTP響應消息的Location屬性中,返回給瀏覽器即可。

輪詢策略。調度伺服器需要維護一個值,用於記錄上次分配的後端伺服器的IP。那麼當新的請求到來時,調度者將請求依次分配給下一臺伺服器。

輪詢策略需要調度者維護一個值用於記錄上次分配的伺服器IP,因此需要額外的開銷;而且,由於這個值屬於互斥資源,那麼當多個請求同時到來時,為了避免線程的安全問題,因此需要鎖定互斥資源,從而降低了性能。而隨機分配策略不需要維護額外的值,也就不存在線程安全問題,因此性能比輪詢要高。

(為什麼我會知道這個?2014年百度校招的開發崗有這道題...)

3. 資料庫訪問壓力大——緩存

二八定律總是在無處不在,80%的業務訪問集中在20%的數據上。來說說緩存。

3.1 CDN

CDN的原理是廣泛採用各種緩存伺服器,將這些緩存伺服器分佈到用戶訪問相對集中的地區或網路中,在用戶訪問網站時,利用全局負載技術將用戶的訪問指向距離最近的工作正常的緩存伺服器上,由緩存伺服器直接響應用戶請求。

未部署CDN前,網路請求路徑如下,在不考慮複雜網路的情況下,從請求到響應需要經過3個節點,6個步驟完成一次用戶訪問操作。

  • 請求:本機網路——>運營商網路——>應用伺服器機房
  • 響應:應用伺服器機房——>運營商網路——>本機網路

部署CDN後的網路路徑如下,在不考慮複雜網路的情況下,從請求到響應需要經過2個節點,2個步驟完成一次用戶訪問操作。

  • 請求:本機網路——>運營商網路
  • 響應:運營商網路——>本機網路

這裡不展開描述,可參考如下回答。

CDN是什麼?使用CDN有什麼優勢??

www.zhihu.com
圖標

3.2 反向代理

在加速網站響應中,通常會用到反向代理。當用戶請求到達中心機房後,首先訪問的伺服器是反向代理伺服器,如果它緩存著用戶請求的資源,就直接返回給用戶。

一個實際的應用是:tornado配置 Nginx 作為反向代理。

首先在Tornado中啟動四個進程:

for mid,port in enumerate(range(9010,9014)):
p=multiprocessing.Process(target=run,args=(mid,port))
jobs.append(p)
p.start()

然後配置nginx反向代理

http
{
# Enumerate all the Tornado servers here
upstream frontends {
server 127.0.0.1:9010;
server 127.0.0.1:9011;
server 127.0.0.1:9012;
server 127.0.0.1:9013;
}

server {
listen 80;

root /opt/nginx/html;

location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://frontends;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

重啟nginx,在瀏覽器中打開伺服器ip的80埠就可以訪問tornado的demo應用了

4. 業務量增長——分散式吧!

重點講講這一部分。

4.1 分散式文件系統

先來看看本地文件系統,本地文件系統是對磁碟數據進行基本管理的一個軟體層。磁碟上的內容,我們習慣以文件這種邏輯單元來進行管理。那文件到底存放在磁碟什麼位置,從哪裡開始,哪裡結束?它創建於什麼時候,最近一次修改於什麼時候?它有多大?磁碟上存了多少文件,還剩多少空間?這些都是文件系統所做的工作了。理論上說,無需文件系統,應用程序也可以直接對磁碟進行操作,但如果這樣的話,要關注太多的底層細節(比如要控制數據寫入到底從哪個扇區開始),同時不同的程序之間很容易出現衝突(一不留神大家都寫到一塊去了)。所以,只要操作系統稍微複雜一點,必定要用文件系統來管理磁碟數據。

引入了文件系統,磁碟上面不僅要存放文件數據本身,還需要有對這些數據進行管理的數據,比如文件起始位置、大小、創建時間等。這些數據又叫做元數據。不同文件系統的元數據是不一樣的。元數據會佔用額外的磁碟空間,但總體比例不會很大,它對功能的實現和性能的提升有非常重要的作用。格式化文件系統,其實就是寫入一些初始化的元數據的過程。

Windows上用的FAT、NTFS,Linux下的ext4、XFS、btrfs都是常見的文件系統。FAT簡單,用得也廣,但功能、性能、對數據的保護度都有所欠缺。NTFS是Windows下推薦的文件系統。Linux中用ext4的人較多,這是大多數Linux發行版的默認文件系統。

再來看看分散式文件系統或集羣文件系統,以Hadoop中的HDFS為力,略說一二。

HDFS採用master/slave架構。一個HDFS集羣是由一個Namenode和一定數目的Datanodes組成。HDFS同樣也有塊(block)的概念,但是大得多,默認是64MB。為何HDFS中的塊如此之大?其目的是為了最小化定址開銷。

NameNode和DataNode

NameNode管理著整個HDFS文件系統的元數據。從架構設計上看,元數據大致分成兩個層次:

  • Namespace管理層,負責管理文件系統中的樹狀目錄結構以及文件與數據塊的映射關係;
  • 塊管理層,負責管理文件系統中文件的物理塊與實際存儲位置的映射關係BlocksMap。、

DataNode是文件系統的工作節點。他們根據需要存儲並檢索數據塊(受客戶端或NameNode調度),並且定期向NameNode發送他們所存儲的塊的列表。

事實上,如果運行NameNode服務的集羣毀壞,文件系統上所有的文件將會丟失,因為我們不知道如何根據DataNode的塊重建哪個文件,因此對NameNode實現容錯非常重要(NameNode的容錯在此不表)。

HDFS讀操作

客戶端要訪問一個文件,首先,客戶端從NameNode中獲得組成該文件數據塊位置列表,即知道數據塊被存儲在哪幾個DataNode上;然後,客戶端直接從DataNode上讀取文件數據。在此過程中,NameNode不參與文件的傳輸。

1)客戶端向namenode發送上傳文件請求,namenode對要上傳目錄和文件進行檢查,判斷是否可以上傳,並向客戶端返回檢查結果。

2)客戶端得到上傳文件的允許後讀取客戶端配置,向namenode請求上傳一個數據塊。

3)namenode會根據客戶端的配置來查詢datanode信息,如果使用默認配置,那麼最終結果會返回同一個機架的兩個datanode和另一個機架的datanode。這稱為「機架感知」策略。

4)客戶端在開始傳輸數據塊之前會把數據緩存在本地,當緩存大小超過了一個數據塊的大小,客戶端就會從namenode獲取要上傳的datanode列表。之後會在客戶端和第一個datanode建立連接開始流式的傳輸數據,這個datanode會一小部分一小部分(4K)的接收數據然後寫入本地倉庫,同時會把這些數據傳輸到第二個datanode,第二個datanode也同樣一小部分一小部分的接收數據並寫入本地倉庫,同時傳輸給第三個datanode,依次類推。這樣逐級調用和返回之後,待這個數據塊傳輸完成客戶端後告訴namenode數據塊傳輸完成,這時候namenode才會更新元數據信息記錄操作日誌。

5)第一個數據塊傳輸完成後會使用同樣的方式傳輸下面的數據塊直到整個文件上傳完成。

HDFS寫操作

客戶端首先需要向NameNode發起寫請求,NameNode會根據文件大小和文件塊配置情況,返回給Client它所管理部分DataNode的信息。最後,Client將文件劃分為多個文件塊,根據DataNode的地址信息,按順序寫入到每一個DataNode塊中。

1)客戶端向namenode發起RPC調用,請求讀取文件數據。

2)namenode檢查文件是否存在,如果存在則獲取文件的元信息(blockid以及對應的datanode列表)。

3)客戶端收到元信息後選取一個網路距離最近的datanode,依次請求讀取每個數據塊。客戶端首先要校檢文件是否損壞,如果損壞,客戶端會選取另外的datanode請求。

4)datanode與客戶端簡歷socket連接,傳輸對應的數據塊,客戶端收到數據緩存到本地,之後寫入文件。

5)依次傳輸剩下的數據塊,直到整個文件合併完成。

有關HDFS更多內容,見下方官網。

HDFS Architecture Guide?

hadoop.apache.org
圖標

4.2 分散式資料庫系統

分散式資料庫是網站資料庫的拆分手段,在單表數據規模非常龐大時才使用。更常用的資料庫拆分手段是業務拆分,即將不同的業務資料庫部署在不同的物理伺服器上,因此分散式資料庫日後再展開論述。

推薦閱讀:

相關文章