FastDFS教程
FastDFS簡介
FastDFS為互聯(lián)網(wǎng)量身定制,充分考慮了冗余備份、負(fù)載均衡、線性擴(kuò)容等機(jī)制,并注重高可用、高性能等指標(biāo),使用FastDFS很容易搭建一套高性能的文件服務(wù)器集群提供文件上傳、下載等服務(wù)。
FastDFS服務(wù)端有兩個角色:跟蹤器(tracker)和存儲節(jié)點(storage)。
跟蹤器主要負(fù)責(zé)調(diào)度工作,在訪問上起負(fù)載均衡的作用。
存儲節(jié)點存儲文件,完成文件管理的所有功能,同時FastDFS同時對文件的metaData(文件屬性列表)進(jìn)行管理。所謂文件的metaData就是文件的相關(guān)屬性,以鍵值對(key value pair)方式表示,如:width=1024,其中的key為width,value為1024。文件meta data是文件屬性列表,可以包含多個鍵值對。
tracker和storage都可以由一臺或多臺服務(wù)器構(gòu)成。tracker和storage中的服務(wù)器均可以隨時增加或下線而不影響線上服務(wù)。其中tracker中的所有服務(wù)器都是對等的,可以根據(jù)服務(wù)器的壓力情況隨時增加或減少。
為了支持大容量,storage采用了分卷(或分組)的組織方式。存儲系統(tǒng)由一個或多個卷組成,卷與卷之間的文件是相互獨立的,所有卷的文件容量累加就是整個存儲系統(tǒng)中的文件容量。一個卷可以由一臺或多臺存儲服務(wù)器組成,一個卷下的存儲服務(wù)器中的文件都是相同的,卷中的多臺存儲服務(wù)器起到了冗余備份和負(fù)載均衡的作用。
在卷中增加服務(wù)器時,同步已有的文件由系統(tǒng)自動完成,同步完成后,系統(tǒng)自動將新增服務(wù)器切換到線上提供服務(wù)。
當(dāng)存儲空間不足或即將耗盡時,可以動態(tài)添加卷。只需要增加一臺或多臺服務(wù)器,并將他們配置為一個新的組,這樣就擴(kuò)大了存儲的容量。
FastDFS中的文件標(biāo)識分為兩個部分:組名和文件名。二者缺一不可。
FastDFS原理
存儲節(jié)點采用了分組(group)的方式。存儲系統(tǒng)由一個或多個group組成,group與group之間的文件是相互獨立的,所有g(shù)roup的文件容量累加就是整個存儲系統(tǒng)中的文件容量。
一個group可以由一臺或多臺存儲服務(wù)器組成,一個group下的存儲服務(wù)器中的文件都是相同的,group中的多臺存儲服務(wù)器起到了冗余備份和負(fù)載均衡的作用(一個組的存儲容量為該組內(nèi)存儲服務(wù)器容量最小的那個,不同組的Storage server之間不會相互通信,同組內(nèi)的Storage server之間會相互連接進(jìn)行文件同步)。
工作流程
文件上傳
Client會先想Tracker詢問存儲地址,Tracker查詢到存儲地址后返回給Client,Client拿著地址直接和對應(yīng)的Storage通信,將文件上傳到storage。
文件下載
同樣,Client會向Tracker詢問地址,并帶上要查詢的文件名和組名,tracker查詢后會將地址返回給Client,client拿到地址和指定Storage通訊并下載文件。
使用docker安裝FastDFS
進(jìn)入storage容器,到storage的配置文件中配置http訪問的端口,配置文件在fdfs_conf目錄下的storage.conf。將文件拷貝出來進(jìn)行修改:docker cp storage:/fdfs_conf/storage.conf ~/,修改storage.conf文件中的tracker_server=你自己的宿主機(jī)ip:22122
將修改后的配置文件拷貝到storage的配置目錄下:docker cp ~/storage.conf storage:/fdfs_conf/
重啟storage容器:docker container restart storage
在docker模擬客戶端上傳文件到storage容器
開啟一個客戶端:docker run -tid --name fdfs_sh --net=host season/fastdfs sh
將之前的storage.conf文件拷貝到容器fdfs_sh:/fdfs_conf/目錄下:docker cp ~/storage.conf fdfs_sh:/fdfs_conf/
創(chuàng)建一個txt文件:
進(jìn)入fdfs_conf目錄,并將文件上傳到storage容器:
root@localhost:/# cd fdfs_conf root@localhost:/fdfs_conf# fdfs_upload_file storage.conf /a.txt返回路徑:
然后去我們對應(yīng)的宿主機(jī)掛載文件夾下查看:
FastDFS配合使用Nginx
使用delron/fastdfs鏡像,這個鏡像中自帶Nginx服務(wù)。
查看docker服務(wù):
正常啟動了兩個服務(wù)。
- 端口8888是默認(rèn)的nginx代理端口
- 端口23000是storage服務(wù)端口
- 端口22122是tracker服務(wù)端口
配置Storage
進(jìn)入storage容器,到storage的配置文件中配置http訪問的端口,配置文件在/etc/fdfs目錄下的storage.conf。
默認(rèn)的端口是8888,可以不修改
配置Nginx
在/usr/local/nginx目錄下,修改nginx.conf文件
默認(rèn)配置如下:(這里可以不用修改)
開啟一個客戶端容器
docker run -it -d --network=host --name fdfs_sh -e TRACKER_SERVER=192.168.1.5:22122 delron/fastdfs sh
然后上傳一個文件到fdfs_sh容器中:docker cp timg.jpg fdfs_sh:/
然后進(jìn)入fdfs_sh容器:docker exec -it fdfs_sh bash
上傳文件:fdfs_upload_file /etc/fdfs/client.conf timg.jpg
返回信息如下:
在瀏覽器中輸入:ip:8088/group1/M00/00/00/wKirGV6yybOAdf9TAABjtCKBG50922.jpg就可以看到這張圖片了。
Springboot代碼實現(xiàn)上傳與下載
添加依賴包pom.xml內(nèi)容如下:
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.github.tobato</groupId><artifactId>fastdfs-client</artifactId><version>1.27.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.68</version></dependency></dependencies>配置文件application.yaml內(nèi)容如下:
fdfs:soTimeout: 1500 #socket連接超時時長connectTimeout: 600 #連接tracker服務(wù)器超時時長reqHost: manager #nginx訪問地址reqPort: 8888 #nginx訪問端口thumbImage: #縮略圖生成參數(shù),可選width: 150height: 150trackerList: #TrackerList參數(shù),支持多個,我這里只有一個,如果有多個在下方加- x.x.x.x:port- manager:22122spring:servlet:multipart:max-request-size: 500MBmax-file-size: 500MB server:port: 8080添加配置文件FdfsConfiguration.java:
package cn.ac.iie.config;import com.github.tobato.fastdfs.FdfsClientConfig; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableMBeanExport; import org.springframework.context.annotation.Import; import org.springframework.jmx.support.RegistrationPolicy;@Configuration @Import(FdfsClientConfig.class) // 導(dǎo)入FastDFS-Client組件 @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING) // 解決jmx重復(fù)注冊bean的問題 public class FdfsConfiguration { }工具類FastDFSClientUtil.java:
package cn.ac.iie.utils;import com.github.tobato.fastdfs.domain.fdfs.StorePath; import com.github.tobato.fastdfs.domain.fdfs.ThumbImageConfig; import com.github.tobato.fastdfs.domain.proto.storage.DownloadCallback; import com.github.tobato.fastdfs.service.FastFileStorageClient; import org.apache.commons.io.FilenameUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile;import java.io.IOException; import java.io.InputStream;@Component public class FastDFSClientUtil {@Value("${fdfs.reqHost}")private String reqHost;@Value("${fdfs.reqPort}")private String reqPort;@Autowiredprivate FastFileStorageClient storageClient;@Autowiredprivate ThumbImageConfig thumbImageConfig; //創(chuàng)建縮略圖 , 縮略圖訪問有問題,暫未解決public String uploadFile(MultipartFile file) throws IOException {StorePath storePath = storageClient.uploadFile(file.getInputStream(),file.getSize(), FilenameUtils.getExtension(file.getOriginalFilename()),null);String path = thumbImageConfig.getThumbImagePath(storePath.getPath()) ;System.out.println("thumbImage :" + path); // 縮略圖訪問有問題,暫未解決System.out.println(storePath);return getResAccessUrl(storePath);}public void delFile(String filePath) {storageClient.deleteFile(filePath);}public InputStream download(String groupName, String path ) {InputStream ins = storageClient.downloadFile(groupName, path, new DownloadCallback<InputStream>(){public InputStream recv(InputStream ins) throws IOException {// 將此ins返回給上面的insreturn ins;}}) ;return ins ;}/*** 封裝文件完整URL地址* @param storePath* @return*/private String getResAccessUrl(StorePath storePath) {System.out.println(storePath.getFullPath());String fileUrl = "http://" + reqHost + ":" + reqPort + "/" + storePath.getFullPath();return fileUrl;}}controller:
package cn.ac.iie.controller;import cn.ac.iie.utils.FastDFSClientUtil; import com.alibaba.fastjson.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;import org.apache.commons.io.IOUtils; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream;@RestController public class UploadController {@Autowiredprivate FastDFSClientUtil dfsClient;@PostMapping("/upload")public JSONObject fdfsUpload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {JSONObject jsonObject = new JSONObject();try {String fileUrl = dfsClient.uploadFile(file);jsonObject.put("url", fileUrl);} catch (IOException e) {e.printStackTrace();}return jsonObject;}/** http://localhost:8080/download?filePath=group1/M00/00/00/wKgIZVzZEF2ATP08ABC9j8AnNSs744.jpg*/@RequestMapping("/download")public void download(String filePath , HttpServletRequest request , HttpServletResponse response) throws IOException {// group1/M00/00/00/wKgIZVzZEF2ATP08ABC9j8AnNSs744.jpgString[] paths = filePath.split("/");String groupName = null;for (String item : paths) {if (item.indexOf("group") != -1) {groupName = item;break ;}}String path = filePath.substring(filePath.indexOf(groupName) + groupName.length() + 1, filePath.length());InputStream input = dfsClient.download(groupName, path);//根據(jù)文件名獲取 MIME 類型String fileName = paths[paths.length-1] ;System.out.println("fileName :" + fileName); // wKgIZVzZEF2ATP08ABC9j8AnNSs744.jpgString contentType = request.getServletContext().getMimeType(fileName);String contentDisposition = "attachment;filename=" + fileName;// 設(shè)置頭response.setHeader("Content-Type",contentType);response.setHeader("Content-Disposition",contentDisposition);// 獲取綁定了客戶端的流ServletOutputStream output = response.getOutputStream();// 把輸入流中的數(shù)據(jù)寫入到輸出流中IOUtils.copy(input,output);input.close();}@RequestMapping("/deleteFile")public String delFile(String filePath , HttpServletRequest request ,HttpServletResponse response) {try {dfsClient.delFile(filePath);} catch(Exception e) {// 文件不存在報異常 : com.github.tobato.fastdfs.exception.FdfsServerException: 錯誤碼:2,錯誤信息:找不到節(jié)點或文件// e.printStackTrace();}request.setAttribute("msg", "成功刪除,'" + filePath);return "index";}}總結(jié)
- 上一篇: 斥资超 86 亿美元,迪士尼即将拥有 H
- 下一篇: 美国 AI 公司将打造漂浮在海上的计算平