背景
傳統Web應用中所有的功能部署在一起,圖片、文件也在一台服務器;應用微服務架構后,服務之間的圖片共享通過FTP+Nginx靜態資源的方式進行訪問,文件共享通過nfs磁盤掛載的方式進行訪問,無論是單體架構還是微服務架構下的應用都存在大量圖片、文件讀寫操作,但是昂貴的磁盤空間、高性能服務器無疑增加了運營成本。
所以我們希望文件服務也能微服務、獨立化,這樣既能降低運營成本,又能對文件進行統一的管理和維護,所以搭建獨立的文件服務是解決文件共享、釋放業務系統壓力的最優選擇。於是便誕生了隨行付分布式文件系統簡稱OSS(Object Storage Service),提供的海量、安全、低成本、高可靠的雲存儲服務。它具有與平台無關的RESTful API接口,能夠提供數據可靠性和服務可用性。
文件服務的意義
隨着互聯網圖片、視頻時代的到來,對文件的處理成為各個業務系統面臨的巨大挑戰,沒有文件服務器之前,系統之間處理圖片的方式大相徑庭:FTP、NFS、數據庫存儲等等,雖然都實現了對文件的存儲、訪問,但是系統之間很難達到文件共享,所以文件服務可以形成一個統一的訪問標准,降低各個系統之間的互相依賴,提高開發效率、釋放業務系統壓力,所以文件服務的意義如下:
降低WEB服務器壓力
分擔業務服務器的I0、流程負載,將耗費資源的文件訪問、讀寫操作分離到文件服務器,可以提高服務器的性能和穩定性,降低WEB服務器成本。
獨立服務易擴展
文件服務像微服務架構獨立化,可以有針對性的進行配置提高性能;獨立域名讓圖片管理、CDN緩存文件更方便,隨時擴展文件服務器數量,即不影響業務又能增加文件服務器並發訪問。
統一訪問格式
開發者無需關心存儲路徑、存儲介質、文件備份等,豐富的API幫助系統快速存儲、共享文件,提高項目開發速度。
安全認證
文件服務對資源訪問可以增加認證、權限等安全措施,防止服務器資源被盜用,有效的隔離了數據訪問。
文件服務基本概念
為便於更好的理解對象存儲OSS,需要了解對象存儲中的幾個概念。
對象/文件(Object)
對象是OSS存儲數據的基本單元,也被稱為OSS的文件。對象由元信息(Object Meta),用戶數據(Data)和文件名(Key)組成。對象由存儲空間內部唯一的Key來標識。對象元信息是一個鍵值對,表示了對象的一些屬性,比如最后修改時間、大小等信息,同時用戶也可以在元信息中存儲一些自定義的信息。對象的生命周期是從上傳成功到被刪除為止。在整個生命周期內,對象信息不可變更。重復上傳同名的對象會覆蓋之前的對象,因此,OSS 不支持修改文件的部分內容等操作。
存儲空間(Bucket)
存儲空間是用於存儲對象(Object)的容器,所有的對象都必須隸屬於某個存儲空間。可以設置和修改存儲空間屬性用來控制地域、訪問權限、生命周期等,這些屬性設置直接作用於該存儲空間內所有對象,因此可以通過靈活創建不同的存儲空間來完成不同的管理功能。
同一個存儲空間的內部是扁平的,沒有文件系統的目錄等概念,所有的對象都直接隸屬於其對應的存儲空間。
每個用戶可以擁有多個存儲空間。
存儲空間的名稱在 OSS 范圍內必須是全局唯一的,一旦創建之后無法修改名稱。
存儲空間內部的對象數目沒有限制。
訪問密鑰(AppKey & AppSecret)
AppKey代表應用身份,AppSecret即應用密鑰,用於生成簽名認證,請求文件服務時必須要傳遞appkey和簽名生產的token,網關根據請求驗證請求的合法性性和時效性。
文件服務的功能
應用場景 功能描述
上傳文件 創建存儲空間后,您可以上傳任意文件到該存儲空間
搜索文件 可以在存儲空間中搜索文件或文件夾
查看或下載文件 通過文件 URL 查看或者下載文件
刪除文件或文件夾 刪除單個或者多個文件/文件夾,還可以刪除分片上傳產生的碎片,節省存儲空間
訪問權限 可以通過應用授權和桶授權的方式,授予存儲空間和對象訪問權限的訪問策略
訪問信息 自動記錄對OSS資源的詳細訪問信息
防盜鏈 防止OSS上的數據被其他人盜用,設置防盜鏈
監控服務 預警OSS服務使用情況的實時信息,如基本的系統運行狀態和性能
API和SDK OSS 提供 RESTful API和各種語言的SDK開發包方便您快速進行二次開發
架構設計
OSS以分布式文件系統ceph作為底層存儲,支持SDK或者瀏覽器以http的形式上傳和下載文件,網關負責路由訪問請求到文件服務集群ossWork,ossWork生成文件唯一保存路徑后存儲文件到ceph,並返回加密后訪問地址給用戶。 架構圖如下:
原理介紹:
1.OSS采用OpenResty作為網關處理請求轉發和校驗,OpenResty匯聚了nginx的核心功能模塊,還支持lua腳本方便對nginx功能的擴展,而且nginx多路復用機制和非阻塞的IO非常適合耗時短、業務簡單的校驗操作:權限驗證、防盜鏈、黑白名單等,不僅如此nginx還能作為網關的緩存,這些特性極大提升了網關性能和並發訪問。
2.ossWork作為文件服務,處理圖片水印、縮放、url加密、解密等,還封裝與底層存儲的交互。
3.ossKeeper作為文件管理后台,負責權限接入注冊、申請、監控報表展示。
4.ossMonitor日志收集、計算匯總、數據存儲。
5.ossBackup負責文件異步備份。
oss子系統相關流程圖如下:
核心實現
OSS主要為隨行付各個業務系統提供文件共享和訪問服務,並且可以按應用統計流量、命中率、空間等指標。下面將介紹OSS核心功能以及實現。主要包括:緩存、用戶認證、權限管理、url加密解密、監控統計等。
緩存
提升性能的關鍵是緩存,OSS采用二級緩存:瀏覽器緩存、網關層緩存提升響應速度,具體如下:
第一次請求文件時,OSS返回文件給瀏覽器http的狀態碼是200
第二次請求時同一個文件時,服務器根據請求中HTTP協議中的max-age/Expires,判斷文件未修改,返回狀態碼403,告訴瀏覽器可以繼續使用本地緩存
第三次請求F5強制刷新,網關根據If-Modified-Since、Cache-Control:no-cache和Pragma:no-cache等信息重新返回nginx中緩存文件
第四次請求url時,requesturi不一樣但文件是同一個,nginx根據requesturi判斷網關中無此文件,請求底層存儲返回文件。
用戶認證
為避免文件、圖片盜用,浪費服務器流量和IO等資源,OSS采用防盜鏈的方式認證請求。每個使用OSS服務的系統都需要進行注冊、申請應用,成功后自動生成appkey、appsecret。appkey代表應用身份,appsecret即應用密鑰,用於生成簽名認證,請求文件服務時必須要傳遞appkey和簽名生產的token,網關根據請求驗證請求的合法性性和時效性。后台管理appkey的生成及防盜鏈的簽名如下圖:
手機終端認證
手機端認證同server端一樣,但是由於賬戶信息的安全問題,appkey和appsecret只能保存在服務端,手機終端訪問oss時無法生成簽名,那么手機端如何訪問oss服務?這里我們采用oauth認證的原理:
app用戶登錄終端系統,App發送請求OSS request請求
server收到請求后,向OSS申請資源的臨時授權token
OSS接收授權請求后,生成臨時訪問token返回給server
server組裝文件地址與臨時的token,返回給app
權限管理
每個應用有了自己的appkey,文件訪問時通過appkey驗證身份,默認文件的歸屬只有上傳者可以查看或修改,上傳者可以授權給其他賬戶,通過在后台管理-權限配置功能授權授權的級別分為:修改、查看、刪除,不同的級別對應不同的操作,后台管理系統會實時同步文件權限到文件系統。這樣其他賬戶只需要傳遞自身的appkey就可以對此文件訪問。具體流程及具體配置如下:
URL加解密、規范(實現參考)
底層存儲中的文件名稱必須全局唯一,oss采用自定義算法生成文件名稱,命名規范=時間戳+分隔符+線程ID+分隔符+進程ID+分隔符+客戶端IP,URL規范圖解如下:
為了保證oss服務器的安全,防止程序文件和目錄外泄,oss對url進行了私有協議的加密,按分隔符“_”對每一項進行base64編碼,再按62位字典碼加密生成加后的url,當然也有其他的算法實現,是要實現url加密即可。解密是對加密的逆向操作,解密后的url即為儲存的訪問url。服務端的規范樣例及客戶端的url規范樣例:
oss統計監控
為保證文件服務的質量和可靠性,系統的監控和告警是必不可少的,各個階段的運行信息,以日志的形式寫到文件中,再使用Flume日志收集組件采集各個子系統的日志,按日志類別直接發送到kafka的不同topic,ossMonitor讀取kafka中消息,以時間為單位計算流量、命中率,以空間為單位統計使用率,根據上傳日志是否有異常發送告警郵件,流程如下:
Flume是由cloudera軟件公司產出的可分布式日志收集系統,由source,channel,sink三個組件組成:
Source:
從數據發生器接收數據,並將接收的數據以Flume的event格式傳遞給一個或者多個通道channal,Flume提供多種數據接收的方式,比如Avro,Thrift,txt等
Channel:
channal是一種短暫的存儲容器,它將從source處接收到的event格式的數據緩存起來,直到它們被sinks消費掉,它在source和sink間起着一共橋梁的作用,channal是一個完整的事務,這一點保證了數據在收發的時候的一致性. 並且它可以和任意數量的source和sink鏈接. 支持的類型有: JDBC channel , File System channel , Memort channel等.
Sink:
sink將數據存儲到集中存儲器比如Hbase和kafka,它從channals消費數據(events)並將其傳遞給目標地.
Flume配置如下:
gateway.sources = fileEvent
gateway.channels = kafkaChannel
gateway.sinks = loggerSink
For each one of the sources, the type is defined
gateway.sources.fileEvent.type = TAILDIR
gateway.sources.fileEvent.positionFile = / xxx.json
gateway.sources.fileEvent.filegroups = events
gateway.sources.fileEvent.filegroups.events=/xxx.log
gateway.sources.fileEvent.type = spooldir
The channel can be defined as follows.
gateway.sources.fileEvent.channels = kafkaChannel
gateway.sources.fileEvent.channels = kafkaChannel
gateway.sources.fileEvent.spoolDir = /home/app/oss_events
Each sink's type must be defined
gateway.sinks.loggerSink.type = org.apache.flume.sink.kafka.KafkaSink
Specify the channel the sink should use
gateway.sinks.loggerSink.channel = kafkaChannel
gateway.sinks.loggerSink.kafka.bootstrap.servers=xxx.xxx.xxx.xxx:9092
gateway.sinks.loggerSink.kafka.topic=oss-gateway-events
gateway.sinks.loggerSink.kafka.batchSize=20
gateway.sinks.loggerSink.kafka.producer.requiredAcks=1
Each channel's type is defined.
gateway.channels.kafkaChannel.type = memory
Other config values specific to each type of channel(sink or source)
can be defined as well
In this case, it specifies the capacity of the memory channel
gateway.channels.kafkaChannel.capacity = 30000
gateway.channels.kafkaChannel.transactionCapacity = 100
統計圖如下: