大型網站圖片服務器架構的演進

在主流的Web站點中,圖片每每是不可或缺的頁面元素,尤爲在大型網站中,幾乎都將面臨「海量圖片資源」的存儲、訪問等相關技術問題。在針對圖片服務器的架構擴展中,也會歷經不少曲折甚至是血淚教訓(尤爲是早期規劃不足,形成後期架構上很難兼容和擴展)。前端

本文將以一個真實垂直門戶網站的發展歷程,向你們娓娓道來。程序員

構建在Windows平臺之上的網站,每每會被業內衆多技術認爲很「保守」,甚至會有點。很大部分緣由,是因爲微軟技術體系的封閉和部分技術人員的短視形成的(固然,主要仍是人的問題)。因爲長期缺少開源支持,因此不少人只能「閉門造車」,這樣很容易造成思惟侷限性和短板。以圖片服務器爲例子,若是前期沒有容量規劃和可擴展的設計,那麼隨着圖片文件的不斷增多和訪問量的上升,因爲在性能、容錯/容災、擴展性等方面的設計不足,後續將會給開發、運維工做帶來不少問題,嚴重時甚至會影響到網站業務正常運做和互聯網公司的發展(這毫不是在危言聳聽)。web

不少公司之因此選擇Windows(.NET)平臺來構建網站和圖片服務器,很大部分由創始團隊的技術背景決定的,早期的技術人員可能更熟悉.NET,或者團隊的負責人認爲Windows/.NET的易用性、「短平快」的開發模式、人才成本等方面都比較符合創業初期的團隊,天然就選擇了Windows。後期業務發展到必定規模,也很難輕易將總體架構遷移到其它開源平臺上了。固然,對於構建大規模互聯網,更建議首選開源架構,由於有不少成熟的案例和開源生態的支持(也會有不少坑,就看是你本身最早去踩坑,仍是在別人踩了修復以後你再用),避免重複造輪子和支出高額受權費用。對於遷移難度較大的應用,我的比較推薦Linux、Mono、Jexus、Mysql、Memcahed、Redis……混搭的架構,一樣能支撐具備高併發訪問和大數據量等特色的互聯網應用。sql

單機時代的圖片服務器架構(集中式)

初創時期因爲時間緊迫,開發人員水平也頗有限等緣由。因此一般就直接在website文件所在的目錄下,創建1個upload子目錄,用於保存用戶上傳的圖片文件。若是按業務再細分,能夠在upload目錄下再創建不一樣的子目錄來區分。例如:upload\QA,upload\Face等。數據庫

在數據庫表中保存的也是」upload/qa/test.jpg」這類相對路徑。api

用戶的訪問方式以下:瀏覽器

http://www.yourdomain.com/upload/qa/test.jpg緩存

程序上傳和寫入方式:安全

程序員A經過在web.config中配置物理目錄D:\Web\yourdomain\upload  而後經過stream的方式寫入文件;服務器

程序員B經過Server.MapPath等方式,根據相對路徑獲取物理目錄  而後也經過stream的方式寫入文件。

優勢:實現起來最簡單,無需任何複雜技術,就能成功將用戶上傳的文件寫入指定目錄。保存數據庫記錄和訪問起來卻是也很方便。

缺點:上傳方式混亂,嚴重不利於網站的擴展。

針對上述最原始的架構,主要面臨着以下問題:

  1. 隨着upload目錄中文件愈來愈多,所在分區(例如D盤)若是出現容量不足,則很難擴容。只能停機後更換更大容量的存儲設備,再將舊數據導入。
  2. 在部署新版本(部署新版本前經過須要備份)和平常備份website文件的時候,須要同時操做upload目錄中的文件,若是考慮到訪問量上升,後邊部署由多臺Web服務器組成的負載均衡集羣,集羣節點之間若是作好文件實時同步將是個難題。

 

集羣時代的圖片服務器架構(實時同步)

在website站點下面,新建一個名爲upload的虛擬目錄,因爲虛擬目錄的靈活性,能在必定程度上取代物理目錄,併兼容原有的圖片上傳和訪問方式。用戶的訪問方式依然是:

http://www.yourdomain.com/upload/qa/test.jpg

優勢:配置更加靈活,也能兼容老版本的上傳和訪問方式。

由於虛擬目錄,能夠指向本地任意盤符下的任意目錄。這樣一來,還能夠經過接入外置存儲,來進行單機的容量擴展。

缺點:部署成由多臺Web服務器組成的集羣,各個Web服務器(集羣節點)之間(虛擬目錄下的)須要實時的去同步文件,因爲同步效率和實時性的限制,很難保證某一時刻各節點上文件是徹底一致的。

基本架構以下圖所示:

 

從上圖可看出,整個Web服務器架構已經具有「可擴展、高可用」了,主要問題和瓶頸都集中在多臺服務器之間的文件同步上。

 

上述架構中只能在這幾臺Web服務器上互相「增量同步」,這樣一來,就不支持文件的「刪除、更新」操做的同步了。

早期的想法是,在應用程序層面作控制,當用戶請求在web1服務器進行上傳寫入的同時,也同步去調用其它web服務器上的上傳接口,這顯然是得不償失的。因此咱們選擇使用Rsync類的軟件來作定時文件同步的,從而省去了「重複造輪子」的成本,也下降了風險性。

同步操做裏面,通常有比較經典的兩種模型,即推拉模型:所謂「拉」,就是指輪詢地去獲取更新,所謂推,就是發生更改後主動的「推」給其它機器。固然,也能夠採用加高級的事件通知機制來完成此類動做。

在高併發寫入的場景中,同步都會出現效率和實時性問題,並且大量文件同步也是很消耗系統和帶寬資源的(跨網段則更明顯)。  

集羣時代的圖片服務器架構改進(共享存儲)

 沿用虛擬目錄的方式,經過UNC(網絡路徑)的方式實現共享存儲(將upload虛擬目錄指向UNC)

用戶的訪問方式1:

http://www.yourdomain.com/upload/qa/test.jpg

用戶的訪問方式2(能夠配置獨立域名):

http://img.yourdomain.com/upload/qa/test.jpg

支持UNC所在server上配置獨立域名指向,並配置輕量級的web服務器,來實現獨立圖片服務器。

   優勢: 經過UNC(網絡路徑)的方式來進行讀寫操做,能夠避免多服務器之間同步相關的問題。相對來說很靈活,也支持擴容/擴展。支持配置成獨立圖片服務器和域名訪問,也完整兼容舊版本的訪問規則。   

   缺點 :可是UNC配置有些繁瑣,並且會形成必定的(讀寫和安全)性能損失。可能會出現「單點故障」。若是存儲級別沒有raid或者更高級的災備措施,還會形成數據丟失。

基本架構以下圖所示:

 

在早期的不少基於Linux開源架構的網站中,若是不想同步圖片,可能會利用NFS來實現。事實證實,NFS在高併發讀寫和海量存儲方面,效率上存在必定問題,並不是最佳的選擇,因此大部分互聯網公司都不會使用NFS來實現此類應用。固然,也能夠經過Windows自帶的DFS來實現,缺點是「配置複雜,效率未知,並且缺少資料大量的實際案例」。另外,也有一些公司採用FTP或Samba來實現。

 

上面提到的幾種架構,在上傳/下載操做時,都通過了Web服務器(雖然共享存儲的這種架構,也能夠配置獨立域名和站點來提供圖片訪問,但上傳寫入仍然得通過Web服務器上的應用程序來處理),這對Web服務器來說無疑是形成巨大的壓力。因此,更建議使用獨立的圖片服務器和獨立的域名,來提供用戶圖片的上傳和訪問。

獨立圖片服務器/獨立域名的好處

  1. 圖片訪問是很消耗服務器資源的(由於會涉及到操做系統的上下文切換和磁盤I/O操做)。分離出來後,Web/App服務器能夠更專一發揮動態處理的能力。
  2. 獨立存儲,更方便作擴容、容災和數據遷移。
  3. 瀏覽器(相同域名下的)併發策略限制,性能損失。
  4. 訪問圖片時,請求信息中總帶cookie信息,也會形成性能損失。
  5. 方便作圖片訪問請求的負載均衡,方便應用各類緩存策略(HTTP Header、Proxy Cache等),也更加方便遷移到CDN。

......

 

咱們可使用Lighttpd或者Nginx等輕量級的web服務器來架構獨立圖片服務器。

當前的圖片服務器架構(分佈式文件系統+CDN)

在構建當前的圖片服務器架構以前,能夠先完全撇開web服務器,直接配置單獨的圖片服務器/域名。但面臨以下的問題:

  1. 舊圖片數據怎麼辦?可否繼續兼容舊圖片路徑訪問規則?
  2. 獨立的圖片服務器上須要提供單獨的上傳寫入的接口(服務API對外發布),安全問題如何保證?
  3. 同理,假若有多臺獨立圖片服務器,是使用可擴展的共享存儲方案,仍是採用實時同步機制?

 

直到應用級別的(非系統級) DFS(例如FastDFS HDFS MogileFs MooseFS、TFS)的流行,簡化了這個問題:執行冗餘備份、支持自動同步、支持線性擴展、支持主流語言的客戶端api上傳/下載/刪除等操做,部分支持文件索引,部分支持提供Web的方式來訪問。

考慮到各DFS的特色,客戶端API語言支持狀況(須要支持C#),文檔和案例,以及社區的支持度,咱們最終選擇了FastDFS來部署。

惟一的問題是:可能會不兼容舊版本的訪問規則。若是將舊圖片一次性導入FastDFS,但因爲舊圖片訪問路徑分佈存儲在不一樣業務數據庫的各個表中,總體更新起來也十分困難,因此必須得兼容舊版本的訪問規則。架構升級每每比作全新架構更有難度,就是由於還要兼容以前版本的問題。(給飛機在空中換引擎可比造架飛機可貴多)

解決方案以下:

首先,關閉舊版本上傳入口(避免繼續使用致使數據不一致)。將舊圖片數據經過rsync工具一次性遷移到獨立的圖片服務器上(即下圖中描述的Old Image Server)。在最前端(七層代理,如Haproxy、Nginx)用ACL(訪問規則控制),將舊圖片對應URL規則的請求(正則)匹配到,而後將請求直接轉發指定的web 服務器列表,在該列表中的服務器上配置好提供圖片(以Web方式)訪問的站點,並加入緩存策略。這樣實現舊圖片服務器的分離和緩存,兼容了舊圖片的訪問規則並提高舊圖片訪問效率,也避免了實時同步所帶來的問題。

 

總體架構如圖:

 

基於FastDFS的獨立圖片服務器集羣架構,雖然已經很是的成熟,可是因爲國內「南北互聯」和IDC帶寬成本等問題(圖片是很是消耗流量的),咱們最終仍是選擇了商用的CDN技術,實現起來也很是容易,原理其實也很簡單,我這裏只作個簡單的介紹:

將img域名cname到CDN廠商指定的域名上,用戶請求訪問圖片時,則由CDN廠商提供智能DNS解析,將最近的(固然也可能有其它更復雜的策略,例如負載狀況、健康狀態等)服務節點地址返回給用戶,用戶請求到達指定的服務器節點上,該節點上提供了相似Squid/Vanish的代理緩存服務,若是是第一次請求該路徑,則會從源站獲取圖片資源返回客戶端瀏覽器,若是緩存中存在,則直接從緩存中獲取並返回給客戶端瀏覽器,完成請求/響應過程。

因爲採用了商用CDN服務,因此咱們並無考慮用Squid/Vanish來自行構建前置代理緩存。

上面的整個集羣架構,能夠很方便的作橫向擴展,能知足通常垂直領域中大型網站的圖片服務需求(固然,像taobao這樣超大規模的可能另當別論)。經測試,提供圖片訪問的單臺Nginx服務器(至強E5四核CPU、16G內存、SSD),對小靜態頁面(壓縮後大概只有10kb左右的)能夠扛住幾千個併發且毫無壓力。固然,因爲圖片自己體積比純文本的靜態頁面大不少,提供圖片訪問的服務器的抗併發能力,每每會受限於磁盤的I/O處理能力和IDC提供的帶寬。Nginx的抗併發能力仍是很是強的,並且對資源佔用很低,尤爲是處理靜態資源,彷佛都不須要有過多擔憂了。能夠根據實際訪問量的需求,經過調整Nginx的參數,對Linux內核作調優,加入分級緩存策略等手段可以作更大程度的優化,也能夠經過增長服務器或者升級服務器配置來作擴展,最直接的是經過購買更高級的存儲設備和更大的帶寬,以知足更大訪問量的需求。

值得一提的是,在「雲計算」流行的當下,也推薦高速發展期間的網站,使用「雲存儲」這樣的方案,既能幫你解決各種存儲、擴展、備災的問題,又能作好CDN加速。最重要的是,價格也不貴。

總結,有關圖片服務器架構擴展,大體圍繞這些問題展開:

  1. 容量規劃和擴展問題。
  2. 數據的同步、冗餘和容災。
  3. 硬件設備的成本和可靠性(是普通機械硬盤,仍是SSD,或者更高端的存儲設備和方案)。
  4. 文件系統的選擇。根據文件特性(例如文件大小、讀寫比例等)選擇是用ext3/4或者NFS/GFS/TFS這些開源的(分佈式)文件系統。
  5. 圖片的加速訪問。採用商用CDN或者自建的代理緩存、web靜態緩存架構。
  6. 舊圖片路徑和訪問規則的兼容性,應用程序層面的可擴展,上傳和訪問的性能和安全性等。
相關文章
相關標籤/搜索