IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構? 頂 原

友情提示:正文內容整理自架構師丁浪的技術分享,部分觀點可作拋磚引玉之用,可能並非最佳實踐,歡迎留言指正。

1、前言

一個完善的IM系統中通常充斥着大量的圖片內容,包括:用戶頭像、圖片消息、相冊、圖片表情等等,那麼在做服務端架構設計時該如何存儲這些圖片呢?

本文分享的是典型Web應用中大量圖片的服務端存儲加構的演進過程,但基本的技術原理和架構思路對於IM系統而言同樣適用,所以在閱讀時可以根據自已IM的實際架構情況,酌情吸取適合您的內容即可。文中部分觀點可作拋磚引玉之用,可能並非最佳實踐,請勿迷信之。

實際上:舊式的PC端IM中,諸如圖片消息這種業務形態,可能是通過長連接直接推送過去(所謂的實時圖片傳輸嘛),這種情況理論上是不需要服務端存儲的。但現今的主流移動端IM,基於移動網絡抖動大、 不穩定的特性和隨時隨地社交分享的現實,已很少使用實時傳輸這種技術手段。現在主流IM都是本文所述的這種:通過Http短連接從雲(也就是服務端)“拉取”,這種方式的好處是:隨時隨地分享、對網絡穩定性要求低(只要上傳者一次上傳,服務端可長時間存儲,下一個閱讀者通過URL按需隨讀隨取即可,再次分享時只要分享URL而無需再次完整傳輸整個圖片)。

以此類推:IM系統中,實際上還存在其它類似於圖片的小文件存儲需求,比如:語音留言消息中的AMR短音頻文件(有些IM中爲了音質可能使用的是AAC音頻格式,比如易信)、短視頻功能中的小視頻文件等,這些文件的存儲和使用跟圖片文件基本類似,所以考慮到通用性,如果能把這些小文件存儲也納入到圖片的存儲架構中,對於整體系統架構來說(尤其存儲部分)就顯的更通用。所以本文中雖然以圖片存儲爲切入點,但您實際上完全可以套用到基它小文件的存儲上哦。

學習交流:

- 即時通訊開發交流羣:320837163[推薦]

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

(本文同步發佈於:http://www.52im.net/thread-1356-1-1.html

2、相關文章

▼ 跟IM數據存儲架構有關的文章,有如下幾篇,或許對你有用:

騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率

微信海量用戶背後的後臺系統存儲架構(視頻+PPT) [附件下載]

微信後臺基於時間序的海量數據冷熱分級架構設計實踐

現代IM系統中聊天消息的同步和存儲方案探討

▼ IM開發乾貨系列文章適合作爲IM開發熱點問題參考資料(本文是其第11篇):

IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞

IM消息送達保證機制實現(二):保證離線消息的可靠投遞

如何保證IM實時消息的“時序性”與“一致性”?

IM單聊和羣聊中的在線狀態同步應該用“推”還是“拉”?

IM羣聊消息如此複雜,如何保證不丟不重?

一種Android端IM智能心跳算法的設計與實現探討(含樣例代碼)

移動端IM登錄時拉取數據如何作到省流量?

通俗易懂:基於集羣的移動端IM接入層負載均衡方案分享

淺談移動端IM的多點登陸和消息漫遊原理

IM開發基礎知識補課(一):正確理解前置HTTP SSO單點登陸接口的原理

IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?》(本文)

如果您是IM開發初學者,強烈建議首先閱讀《新手入門一篇就夠:從零開發移動端IM》。

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

初創時期由於時間緊迫,開發人員水平很有限。

所以通常就直接在website文件所在的目錄下,建立1個upload子目錄,用於保存用戶上傳的圖片文件:

[1] 如果按業務再細分,可以在upload目錄下再建立不同的子目錄來區分,例如:upload\QA,upload\Face等

[2] 在數據庫表中保存的也是“upload/qa/test.jpg”這類相對路徑;

[3] 用戶的訪問方式如下:http://www.yourdomain.com/upload/qa/test.jpg

程序上傳和寫入方式:

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

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

結果就是:

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

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

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

隨着upload目錄中文件越來越多,所在分區如果出現容量不足,則很難擴容。只能停機後更換更大容量的存儲設備,再將舊數據導入;

在部署新版本(部署新版本前通過需要備份)和日常備份website文件的時候,需要同時操作upload目錄中的文件,如果考慮到訪問量上升,後邊部署由多臺Web服務器組成的負載均衡集羣,集羣節點之間如果做好文件實時同步將是個難題。

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

一個傳統的Web服務端站點下面,新建一個名爲upload的虛擬目錄,由於虛擬目錄的靈活性,能在一定程度上取代物理目錄,併兼容原有的圖片上傳和訪問方式。

用戶的訪問方式依然是:

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

優點:配置更加靈活,也能兼容老版本的上傳和訪問方式。因爲虛擬目錄,可以指向本地任意盤符下的任意目錄。這樣一來,還可以通過接入外置存儲,來進行單機的容量擴展。

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

基本架構如下圖所示:

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

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

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

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

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

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

沿用虛擬目錄的方式,通過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服務器來講無疑是造成巨大的壓力。所以,更建議使用獨立的圖片服務器和獨立的域名,來提供用戶圖片的上傳和訪問。

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

圖片訪問是很消耗服務器資源的(因爲會涉及到操作系統的上下文切換和磁盤I/O操作)。分離出來後,Web/App服務器可以更專注發揮動態處理的能力。

獨立存儲,更方便做擴容、容災和數據遷移;

瀏覽器(相同域名下的)併發策略限制,性能損失;

訪問圖片時,請求信息中總帶cookie信息,也會造成性能損失;

方便做圖片訪問請求的負載均衡,方便應用各種緩存策略(HTTP Header、Proxy Cache等),也更加方便遷移到CDN;

......

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

7、我們當前的圖片服務器架構

當前圖片服務器架構採用分佈式文件系統+CDN。

在構建當前的圖片服務器架構之前,可以先徹底撇開web服務器,直接配置單獨的圖片服務器/域名。

但面臨如下的問題:

舊圖片數據怎麼辦?能否繼續兼容舊圖片路徑訪問規則?

獨立的圖片服務器上需要提供單獨的上傳寫入的接口(服務API對外發布),安全問題如何保證?

同理,假如有多臺獨立圖片服務器,是使用可擴展的共享存儲方案,還是採用實時同步機制?

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

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

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

解決方案如下:

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

整體架構如圖:

8、使用第3方CDN的方案

基於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加速。最重要的是,價格也不貴。

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

容量規劃和擴展問題;

數據的同步、冗餘和容災;

硬件設備的成本和可靠性(是普通機械硬盤,還是SSD,或者更高端的存儲設備和方案);

文件系統的選擇。根據文件特性(例如文件大小、讀寫比例等)選擇是用ext3/4或者NFS/GFS/TFS這些開源的(分佈式)文件系統;

圖片的加速訪問。採用商用CDN或者自建的代理緩存、web靜態緩存架構;

舊圖片路徑和訪問規則的兼容性,應用程序層面的可擴展,上傳和訪問的性能和安全性等。

附錄:更多IM開發文章

[1] 有關IM架構設計:

淺談IM系統的架構設計

簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端

一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)

一套原創分佈式即時通訊(IM)系統理論架構方案

從零到卓越:京東客服即時通訊系統的技術架構演進歷程

蘑菇街即時通訊/IM服務器開發之架構選擇

騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT

微信後臺基於時間序的海量數據冷熱分級架構設計實踐

微信技術總監談架構:微信之道——大道至簡(演講全文)

如何解讀《微信技術總監談架構:微信之道——大道至簡》

快速裂變:見證微信強大後臺架構從0到1的演進歷程(一)

17年的實踐:騰訊海量產品的技術方法論

移動端IM中大規模羣消息的推送如何保證效率、實時性?

現代IM系統中聊天消息的同步和存儲方案探討

IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?

>> 更多同類文章 ……

[2] 有關IM安全的文章:

即時通訊安全篇(一):正確地理解和使用Android端加密算法

即時通訊安全篇(二):探討組合加密算法在IM中的應用

即時通訊安全篇(三):常用加解密算法與通訊安全講解

即時通訊安全篇(四):實例分析Android中密鑰硬編碼的風險

即時通訊安全篇(五):對稱加密技術在Android平臺上的應用實踐

即時通訊安全篇(六):非對稱加密技術的原理與應用實踐

傳輸層安全協議SSL/TLS的Java平臺實現簡介和Demo演示

理論聯繫實際:一套典型的IM通信協議設計詳解(含安全層設計)

微信新一代通信安全解決方案:基於TLS1.3的MMTLS詳解

來自阿里OpenIM:打造安全可靠即時通訊服務的技術實踐分享

簡述實時音視頻聊天中端到端加密(E2EE)的工作原理

移動端安全通信的利器——端到端加密(E2EE)技術詳解

Web端即時通訊安全:跨站點WebSocket劫持漏洞詳解(含示例代碼)

通俗易懂:一篇掌握即時通訊的消息傳輸安全原理

>> 更多同類文章 ……

[3] IM開發綜合文章:

IM開發基礎知識補課:正確理解前置HTTP SSO單點登陸接口的原理

移動端IM中大規模羣消息的推送如何保證效率、實時性?

移動端IM開發需要面對的技術問題

開發IM是自己設計協議用字節流好還是字符流好?

請問有人知道語音留言聊天的主流實現方式嗎?

IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞

IM消息送達保證機制實現(二):保證離線消息的可靠投遞

如何保證IM實時消息的“時序性”與“一致性”?

一個低成本確保IM消息時序的方法探討

IM單聊和羣聊中的在線狀態同步應該用“推”還是“拉”?

IM羣聊消息如此複雜,如何保證不丟不重?

談談移動端 IM 開發中登錄請求的優化

移動端IM登錄時拉取數據如何作到省流量?

淺談移動端IM的多點登陸和消息漫遊原理

完全自已開發的IM該如何設計“失敗重試”機制?

通俗易懂:基於集羣的移動端IM接入層負載均衡方案分享

微信對網絡影響的技術試驗及分析(論文全文)

即時通訊系統的原理、技術和應用(技術論文)

開源IM工程“蘑菇街TeamTalk”的現狀:一場有始無終的開源秀

QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)

QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)

騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率

騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)

騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(下篇)

如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源

基於社交網絡的Yelp是如何實現海量用戶圖片的無損壓縮的?

>> 更多同類文章 ……

(本文同步發佈於:http://www.52im.net/thread-1356-1-1.html

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