本文繼續討論文件服務中的子模塊“存儲模塊”的設計,包括:
之前的架構沒有專門設計存儲,直接使用本地存儲。考慮到後期文件數量可能會增加,本地存儲可能無法支持,本地存儲的安全性得不到保障。為了方便後期擴展,需要設計“存儲”部分。
存儲方式有很多種,比如本地存儲、NAS、分布式存儲。為了支持不同的存儲方式,有必要對“存儲模塊”進行抽象。考慮到“存儲模塊”涉及IO,是壹個比較低級的模塊。“上傳”這個核心模塊不能依賴具體的存儲,這裏需要反過來。
看到紫色的部分。UploadService調用FileInfoRepository存儲FileInfo,FileInfoRepository是壹個接口,由存儲模塊中的實現類實現。
讓我們先來看看本地存儲。最簡單的實現就是直接用IO把文件寫到對應的目錄。然而,本地存儲存在以下問題:
讓我們逐壹解決以上問題。
首先,對於多租戶,在我們的架構中,實際上是對應組的,所以我們可以根據組的不同來劃分目錄。也就是說,不同的租戶有不同的文件根目錄。後期租戶遷移時,可以直接遷移對應的目錄。這也稍微解決了大量單目錄文件的問題。
如何解決單個目錄下文件數量增加訪問速度下降的問題?
如果妳做過分布式系統,那妳想想,我們能不能把單個目錄看成壹個服務器,把訪問目錄裏的文件看成壹個個請求?如果是,那麽解決單個目錄訪問速度慢的問題是否就變成了“如何解決單個服務器過載”的問題?解決服務器負載過重的方法適合解決目錄訪問速度下降的問題嗎?
我們從以下幾個方面來分析壹下:
首先我們來看“服務器負載過重的解決方案”!答案很明顯:調車+負載均衡!
有多少種方法可以平衡分布式服務的負載?
讓我們看看目錄訪問和服務器之間的區別。雖然目錄可以看作是服務器,但兩者還是有區別的:
換句話說,對於目錄,我們不需要考慮創建成本。
那麽高服務器負載的解決方案適合目錄訪問嗎?或者說哪種方式適合目錄訪問?讓我們逐壹分析:
如妳所見,主要問題是創建壹個目錄!如何保證目錄數量變化時不需要調整程序?
事實上,git已經給出了答案:
換句話說,文件是根據sha1哈希的前兩位數字進行分類的。這不僅解決了目錄創建的問題,也解決了文件分發的問題。可能的問題是,“sha1哈希2 80次,可能會發生沖突”。對於壹般的文件系統,似乎不需要擔心這個問題。
解決了“單個目錄下文件太多,導致訪問速度慢”的問題。我們來看下壹個問題:數據安全。
文件數據存儲在計算機磁盤上。如果硬盤損壞,文件可能會丟失。這其實是壹個“單點問題”!
「單點問題」的解決方法是什麽?冗余!
最簡單的方案是定期備份數據。有如下幾種方案:
讓我們壹個壹個地繼續討論。
第壹種是手動備份,這是最低級的方案,當然也是最簡單的,就是有人定時備份。問題是時效性不高,比如壹天備份壹次。如果磁盤在備份前發生故障,將會丟失壹天的數據。同時,恢復非常耗時,並且需要手動處理。
第二種方案是代碼實現,即上傳文件時,程序會自動備份。以上面的架構為例,可以添加壹個BackupListener,上傳完成後通過事件自動備份上傳的文件。同時,下載時要確定文件是否完整,有問題就用備份數據。這種方案時效性有保證,但是數據備份和業務放在壹起,需要編碼,增加了業務代碼量。
第三個方案是libfuse,這是壹個用戶模式的文件系統接口。以下是libfuse的官方簡介:
簡單地說,妳可以用libfuse構建壹個用戶模式的文件系統。在老東家做日誌分析平臺之前,我用libfuse收集日誌。總體框架如下:
業務系統將日誌寫入掛載的用戶態文件系統,用戶態文件系統自動轉發到後續處理中間件:redis、消息隊列、文件系統。
這裏也可以使用類似的功能,即文件上傳後,自動備份用戶態文件系統。這個方案將文件歸檔邏輯與業務邏輯分離開來。
最後壹種方案是RAID,這是壹種廉價的冗余磁盤陣列。RAID不僅可以備份文件,還支持並發讀寫,提高了上傳下載速率。
常用的RAID包括:RAID0、RAID1、RAID01/RAID10、RAID5和RAID6等。讓我們看看這些RAID類型的特征,以及它們是否適用於我們的文件服務。妳會發現從RAID0到RAID6,是壹個從單點到分布式的過程。
看下面兩張圖更好理解:
無論是RAID10還是RAID01,磁盤的使用效率都不高。那麽如何提高磁盤利用率呢?有RAID3。
對於本地存儲來說,RAID是壹種比較實用的解決方案,它不僅可以提高數據安全性,快速擴展,還可以提高讀寫速度。但是無論擴展多少磁盤,容量還是相對有限的,吞吐量也是相對有限的。同時,因為還是單點,如果文件服務本身掛起,會導致單點故障。於是就有了分布式文件系統。
分布式文件系統下次再單獨討論!
最後做個廣告,幫壹個朋友開“零基礎Unity3D遊戲開發”專欄,適合沒有基礎,想從事遊戲開發的小白!朋友從事遊戲多年,開發了各種遊戲,收了30多個徒弟。