一、單機時代的圖片服務器架構html
初創時期因爲時間緊迫,開發人員水平也頗有限等緣由。因此一般就直接在website文件所在的目錄下,創建1個upload子目錄,用於保存用戶上傳的圖片文件。若是按業務再細分,能夠在upload目錄下再創建不一樣的子目錄來區分。例如:upload\QA,upload\Face等java
優勢:實現起來最簡單,無需任何複雜技術,就能成功將用戶上傳的文件寫入指定目錄。保存數據庫記錄和訪問起來卻是也很方便。linux
缺點:上傳方式混亂,嚴重不利於網站的擴展。nginx
二、單獨立文件服務器web
隨着公司的業務不斷的發展,將服務和文件放在同一服務器下面的弊端愈來愈明顯;這個時候就該上線獨立的圖片服務器系統;經過ftp或者ssh工具將文件上傳到圖片服務器的某個目錄下面,在經過ngnix或者apache服務器來作圖片的訪問,給圖片服務器配置獨立的子域名,例如 img.xx.com。在業務處理文件時經過ftp或者ssh將文件上傳到文件服務器,返回給程序一個獨立域名的圖片url地址,網站正常訪問的時候就經過這個URL地址來訪問文件。算法
優勢:圖片訪問是很消耗服務器資源的(由於會涉及到操做系統的上下文切換和磁盤I/O操做)。分離出來後,Web/App服務器能夠更專一發揮動態處理的能力;獨立存儲,更方便作擴容、容災和數據遷移;方便作圖片訪問請求的負載均衡,方便應用各類緩存策略(HTTP Header、Proxy Cache等),也更加方便遷移到CDN。spring
缺點:單機存在性能瓶頸,容災、垂直擴展性稍差數據庫
三、分佈式文件系統apache
業務繼續發展,單獨單臺的服務器存儲和響應也很快到達了瓶頸,新的業務要求,文件訪問高響應性,高可用性來響應業務對系統的要求。分佈式文件系統,通常分爲三塊內容來配合,服務的存儲、訪問的仲裁系統,文件存儲系統,文件的容災系統來構成,總裁系統至關於文件服務器的大腦,根據必定的算法來決定文件存儲的位置,文件存儲系統負責報錯文件,容災系統負責文件系統和本身的相互備份。緩存
優勢:擴展能力: 毫無疑問,擴展能力是一個分佈式文件系統最重要的特色;高可用性: 在分佈式文件系統中,高可用性包含兩層,一是整個文件系統的可用性,二是數據的完整和一致性;彈性存儲: 能夠根據業務須要靈活地增長或縮減數據存儲以及增刪存儲池中的資源,而不須要中斷系統運行
缺點:系統複雜度稍高,須要更多服務器
一、什麼是FastDFS
FastDFS是一個開源的輕量級分佈式文件系統。它解決了大數據量存儲和負載均衡等問題。特別適合以中小文件(建議範圍:4KB < file_size <500MB)爲載體的在線服務,如相冊網站、視頻網站等等。在UC基於FastDFS開發向用戶提供了:網盤,社區,廣告和應用下載等業務的存儲服務。
二、FastDFS架構和原理
FastDFS服務端有三個角色:跟蹤服務器(tracker server)、存儲服務器(storage server)和客戶端(client)。
tracker server:跟蹤服務器,主要作調度工做,起負載均衡的做用。在內存中記錄集羣中全部存儲組和存儲服務器的狀態信息,是客戶端和數據服務器交互的樞紐。相比GFS中的master更爲精簡,不記錄文件索引信息,佔用的內存量不多。
storage server:存儲服務器(又稱:存儲節點或數據服務器),文件和文件屬性(meta data)都保存到存儲服務器上。Storage server直接利用OS的文件系統調用管理文件。
client:客戶端,做爲業務請求的發起方,經過專有接口,使用TCP/IP協議與跟蹤器服務器或存儲節點進行數據交互。
Tracker至關於FastDFS的大腦,不管是上傳仍是下載都是經過tracker來分配資源;客戶端通常可使用ngnix等靜態服務器來調用或者作一部分的緩存;存儲服務器內部分爲卷(或者叫作組),卷於卷之間是平行的關係,能夠根據資源的時候狀況隨時增長,卷內服務器文件相互同步備份,以達到容災的目的
上傳機制:
首先客戶端請求Tracker服務獲取到存儲服務器的ip地址和端口,而後客戶端根據返回的IP地址和端口號請求上傳文件,存儲服務器接收到請求後,生產文件file_id而且將文件內容寫入磁盤返回給客戶端file_id和路徑信息、文件名,客戶端保存相關信息上傳完畢
下載機制:
客戶端帶上文件名信息請求Tracker服務獲取到存儲服務器的ip地址和端口,而後客戶端根據返回的IP地址和端口號請求下載文件,存儲服務器接收到請求後返回文件給客戶端。
三、如何搭建fastDFS
請參考如下文章:
地址:http://www.linux178.com/storage/fastdfs-nginx-cache.html
四、使用java調用fastDFS
如下代碼是一個spring mvc中一個完整的上傳請求
@RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public Object upload(@RequestParam MultipartFile file) { UploadResponse res = new UploadResponse(); try { if(file.isEmpty()){ res.setRet_code(UserCodeEnum.ERR_FILE_NULL.getCode()); res.setRet_msg(UserCodeEnum.ERR_FILE_NULL.getDesc()); }else{ logger.info("UserController-upload-request-file=" + file.getOriginalFilename()); String tempFileName = file.getOriginalFilename(); //fastDFS方式 ClassPathResource cpr = new ClassPathResource("fdfs_client.conf"); ClientGlobal.init(cpr.getClassLoader().getResource("fdfs_client.conf").getPath()); byte[] fileBuff = file.getBytes(); String fileId = ""; String fileExtName = tempFileName.substring(tempFileName.lastIndexOf(".")); //創建鏈接 TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; StorageClient1 client = new StorageClient1(trackerServer, storageServer); //設置元信息 NameValuePair[] metaList = new NameValuePair[3]; metaList[0] = new NameValuePair("fileName", tempFileName); metaList[1] = new NameValuePair("fileExtName", fileExtName); metaList[2] = new NameValuePair("fileLength", String.valueOf(file.getSize())); //上傳文件 fileId = client.upload_file1(fileBuff, fileExtName, metaList); res.setHead_img(UserConstants.FILE_IMG_URL+fileId); res.setRet_code(UserCodeEnum.SUCCESS.getCode()); res.setRet_msg(UserCodeEnum.SUCCESS.getDesc()); } logger.info("UserController-upload-response-" + JsonUtils.o2j(res)); } catch (Exception e) { res.setRet_code(UserCodeEnum.ERR_UNKNOWN.getCode()); res.setRet_msg(UserCodeEnum.ERR_UNKNOWN.getDesc()); logger.error("UserController-upload-error", e); } return res; }
fastDFS java客戶端配置文件fdfs_client.conf配置以下:
connect_timeout = 30 network_timeout = 60 charset = ISO8859-1 http.tracker_http_port = 8090 http.anti_steal_token = no http.secret_key = 123456 tracker_server = 192.168.11.***:22122
參考: