FastDFS是一個開源的輕量級分散式檔案系統,它對檔案進行管理,功能包括:檔案儲存、檔案同步、檔案訪問(檔案上傳、檔案下載)等,解決了大容量儲存和負載均衡的問題,同時也能做到在叢集環境下一臺機子上傳檔案,同時該組下的其他節點下也備份了上傳的檔案。做分散式系統開發時,其中要解決的一個問題就是圖片、音視訊、檔案共享的問題和資料備份,分散式檔案系統正好可以解決這個需求。FastDFS的服務主要有兩個角色Tracker和Storage,Tracker服務用於負責排程storage節點與client通訊,在訪問上起負載均衡的作用,和記錄storage節點的執行狀態,是連線client和storage節點的樞紐,Storage用於儲存檔案
-
FastDFS叢集部署
-
整體部署模組圖
-
環境準備
名稱 描述 centos系統版本 6.9 libfatscommon FastDFS分離出的一些公用函式包 FastDFS FastDFS主程式 fastdfs-nginx-module FastDFS和nginx的關聯模組 nginx nginx1.15.5 - 安裝編譯環境
yum install git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel wget vim -y 複製程式碼
-
磁碟安裝路徑說明
說明 位置 FastDFS所以安裝包安裝位置 /usr/local/src tracker資料 /data/fdfs/tracker Storage資料 /data/fdfs/Storage 配置檔案路徑 /etc/fdfs
-
安裝libfatscommon
-
解壓、安裝
unzip libfastcommon-master.zip cd libfastcommon-master ./make.sh && ./make.sh install #編譯安裝 複製程式碼
-
安裝FastDFS
-
解壓、安裝
unzip fastdfs-master.zip cd fastdfs-master ./make.sh && ./make.sh install #編譯安裝 cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf #客戶端檔案,測試用 cp /usr/local/src/fastdfs/conf/http.conf /etc/fdfs/ #供nginx訪問使用 cp /usr/local/src/fastdfs/conf/mime.types /etc/fdfs/ #供nginx訪問使用 複製程式碼
-
安裝fastdfs-nginx-module
-
解壓、安裝
unzip fastdfs-nginx-module-master.zip cp /usr/local/src/fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs #複製配置檔案到fdfs目錄 複製程式碼
-
安裝nginx
-
解壓、安裝
tar -zxvf nginx-1.15.5.tar.gz cd nginx-1.15.5 #新增fastdfs-nginx-module模組 ./configure --add-module=/usr/local/src/fastdfs-nginx-module-master/src/ make && make install #編譯安裝 複製程式碼
-
FastDFS叢集部署配置
- tracker配置
#伺服器ip為 xxx.xxx.78.12, xxx.xxx.78.13 vim /etc/fdfs/tracker.conf #需要修改的內容如下 port=22122 # tracker伺服器埠(預設22122,一般不修改) base_path=/data/fdfs/tracker #儲存日誌和資料的根目錄 複製程式碼
-
Storage配置
vim /etc/fdfs/storage.conf #需要修改的內容如下 port=23000 # storage服務埠(預設23000,一般不修改) base_path=/data/fdfs/storage # 資料和日誌檔案儲存根目錄 store_path0=/data/fdfs/storage # 第一個儲存目錄 tracker_server=xxx.xxx.78.12:22122 # 伺服器1 tracker_server=xxx.xxx.78.13:22122 # 伺服器2 http.server_port=8888 # http訪問檔案的埠(預設8888,看情況修改,和nginx中保持一致) 複製程式碼
- client配置
vim /etc/fdfs/client.conf #需要修改的內容如下 base_path=/home/moe/dfs tracker_server=xxx.xxx.78.12:22122 # 伺服器1 tracker_server=xxx.xxx.78.13:22122 # 伺服器2 複製程式碼
- 配置nginx訪問
vim /etc/fdfs/mod_fastdfs.conf #需要修改的內容如下 tracker_server=xxx.xxx.78.12:22122 # 伺服器1 tracker_server=xxx.xxx.78.13:22122 # 伺服器2 url_have_group_name=true store_path0=/data/fdfs/storage #配置nginx.config vim /usr/local/nginx/conf/nginx.conf #新增如下配置 server { listen 8888; ## 該埠為storage.conf中的http.server_port相同 server_name localhost; location ~/group[0-9]/ { ngx_fastdfs_module; } ...... ...... error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } 複製程式碼
-
啟動服務、測試
啟動之前我們還需要在防火牆開通埠 vim /etc/sysconfig/iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 22122 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 23000 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 8888 -j ACCEPT service iptables restart #重啟防火牆 複製程式碼
-
每個服務的啟動、關閉和重啟操作
#tracker /etc/init.d/fdfs_trackerd start #啟動tracker服務 /etc/init.d/fdfs_trackerd restart #重啟動tracker服務 /etc/init.d/fdfs_trackerd stop #停止tracker服務 chkconfig fdfs_trackerd on #自啟動tracker服務 #storage /etc/init.d/fdfs_storaged start #啟動storage服務 /etc/init.d/fdfs_storaged restart #重動storage服務 /etc/init.d/fdfs_storaged stop #停止動storage服務 chkconfig fdfs_storaged on #自啟動storage服務 #nginx /usr/local/nginx/sbin/nginx #啟動nginx /usr/local/nginx/sbin/nginx -s reload #重啟nginx /usr/local/nginx/sbin/nginx -s stop #停止nginx 複製程式碼
-
檢測叢集
# 會顯示會有幾臺storage伺服器,有2臺就會顯示 Storage 1-Storage 2的詳細資訊 /usr/bin/fdfs_monitor /etc/fdfs/storage.conf 複製程式碼
-
圖片上傳測試
#上傳成功返回 檔案訪問 ID # fdfs_upload_file 客戶端配置檔案 上傳檔案路徑 fdfs_upload_file /etc/fdfs/client.conf /data/test.png 複製程式碼
-
測試檔案訪問
http://xxx.xxx.78.12/group1/M00/00/00/rB9ODFvXuSiAWBYBAALSAkm_6RQ360.png http://xxx.xxx.78.13/group1/M00/00/00/rB9ODFvXuSiAWBYBAALSAkm_6RQ360.png 複製程式碼
測試nginx預設埠80 訪問剛剛上傳的檔案,兩個地址都能訪問通一個檔案,達到資料備份目的。
-
至此,FastDFS伺服器部署完成
-
FastDFS客戶端整合到SpringBoot
-
首先根據官方原始碼提示,我們先下載原始碼使用maven編譯成jar包放到公司maven私服(Nexus),或者你本地的maven私服(也有其他ant等方式,具體請檢視github) FastDFS-java-client-SDK原始碼下載地址
#編譯jar包(解壓下載的FastDFS-java-client-SDK原始碼,使用mvn命令需要先有maven環境) mvn clean install 複製程式碼
-
maven專案pom.xml中新增依賴
<dependency> <groupId>org.csource</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27-SNAPSHOT</version> </dependency> 複製程式碼
-
接下來我們在專案resources目錄下新增fdfs_client.conf檔案
connect_timeout = 30 network_timeout = 30 charset = UTF-8 http.tracker_http_port = 80 http.anti_steal_token = no http.secret_key = 123456 #前面配置的叢集tracker伺服器地址 tracker_server = xxx.xxx.78.12:22122 tracker_server = xxx.xxx.78.13:22122 複製程式碼
-
寫一個上傳檔案物件類
/** * @Author: maoqitian * @Date: 2018/10/26 0026 17:57 * @Description: FastDFS 檔案類 */ public class FastDFSFileEntity { //檔名稱 private String name; //內容 private byte[] content; //檔案型別 private String ext; //md5值 private String md5; //作者 private String author; public FastDFSFileEntity(String name, byte[] content, String ext, String height, String width, String author) { super(); this.name = name; this.content = content; this.ext = ext; this.author = author; } public FastDFSFileEntity(String name, byte[] content, String ext) { super(); this.name = name; this.content = content; this.ext = ext; } public String getName() { return name; } public void setName(String name) { this.name = name; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } public String getExt() { return ext; } public void setExt(String ext) { this.ext = ext; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } } 複製程式碼
-
編寫FastDFS操作類,主要是載入初始化配置Tracker伺服器,檔案上傳,下載,刪除等操作工具類
/** * @Author: maoqitian * @Date: 2018/10/29 0029 9:30 * @Description: FastDFS 操作類 */ public class FastDFSClient { private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class); //雙重守護單例 private static volatile FastDFSClient mInstance; /** * 載入配置資訊 **/ static { try { String filePath=new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath(); ClientGlobal.init(filePath); }catch (Exception e){ logger.error("FastDFS Client Init Fail!",e); } } private FastDFSClient(){ } public static FastDFSClient getInstance(){ if(mInstance == null){ synchronized (FastDFSClient.class){ if(mInstance == null){ mInstance=new FastDFSClient(); } } } return mInstance; } /** * @Author maoqitian * @Description 上傳檔案 * @Date 2018/10/29 0029 9:42 * @Param [fastDFSFileEntity] * @return java.lang.String[] **/ public String[] upload(FastDFSFileEntity file){ logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length); NameValuePair[] metalist=new NameValuePair[1]; metalist[0]=new NameValuePair("author",file.getAuthor()); long startTime = System.currentTimeMillis(); String[] uploadResults= null; StorageClient storageClient=null; try { storageClient=getTrackerClient(); uploadResults = storageClient.upload_file(file.getContent(),file.getExt(),metalist); }catch (IOException e){ logger.error("IO Exception when uploadind the file:"+file.getName(),e); } catch (Exception e){ logger.error("Non IO Exception when uploadind the file:"+file.getName(),e); } logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms"); if(uploadResults==null && storageClient!=null){ logger.error("upload file fail, error code:" + storageClient.getErrorCode()); } String groupName = uploadResults[0]; String remoteFileName = uploadResults[1]; logger.info("upload file successfully!!!" + "group_name:" + groupName + ", remoteFileName:" + " " + remoteFileName); return uploadResults; } public FileInfo getFile(String groupName, String remoteFileName) { try { StorageClient storageClient = getTrackerClient(); return storageClient.get_file_info(groupName, remoteFileName); } catch (IOException e) { logger.error("IO Exception: Get File from Fast DFS failed", e); } catch (Exception e) { logger.error("Non IO Exception: Get File from Fast DFS failed", e); } return null; } public InputStream downFile(String groupName, String remoteFileName) { try { StorageClient storageClient = getTrackerClient(); byte[] fileByte = storageClient.download_file(groupName, remoteFileName); InputStream ins = new ByteArrayInputStream(fileByte); return ins; } catch (IOException e) { logger.error("IO Exception: Get File from Fast DFS failed", e); } catch (Exception e) { logger.error("Non IO Exception: Get File from Fast DFS failed", e); } return null; } /** * @Author maoqitian * @Description * @Date 2018/10/31 0031 11:19 * @Param [remoteFileName] * @return int -1 失敗 0成功 **/ public int deleteFile(String remoteFileName) throws Exception { StorageClient storageClient = getTrackerClient(); int i = storageClient.delete_file("group1", remoteFileName); logger.info("delete file successfully!!!" + i); return i; } public StorageServer[] getStoreStorages(String groupName) throws IOException { TrackerClient trackerClient = new TrackerClient(); TrackerServer trackerServer = trackerClient.getConnection(); return trackerClient.getStoreStorages(trackerServer, groupName); } public ServerInfo[] getFetchStorages(String groupName, String remoteFileName) throws IOException { TrackerClient trackerClient = new TrackerClient(); TrackerServer trackerServer = trackerClient.getConnection(); return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName); } public String getTrackerUrl() throws IOException { return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port()+"/"; } /** * @Author maoqitian * @Description 獲取 StorageClient * @Date 2018/10/29 0029 10:33 * @Param [] * @return org.csource.fastdfs.StorageClient **/ private StorageClient getTrackerClient() throws IOException{ TrackerServer trackerServer=getTrackerServer(); StorageClient storageClient=new StorageClient(trackerServer,null); return storageClient; } /** * @Author maoqitian * @Description 獲取 TrackerServer * @Date 2018/10/29 0029 10:34 * @Param [] * @return org.csource.fastdfs.TrackerServer **/ private TrackerServer getTrackerServer() throws IOException { TrackerClient trackerClient=new TrackerClient(); TrackerServer trackerServer = trackerClient.getConnection(); return trackerServer; } 複製程式碼
-
Controller編寫,接收請求並上傳檔案返回檔案訪問路徑(這裡寫一個檔案上傳的例子,其他檔案下載,刪除等功能可根據自己需求進行編寫)
/** * @Author maoqitian * @Description 上傳檔案 * @Date 2018/10/30 0030 15:07 * @Param [file] * @return com.gxxmt.common.utils.ResultApi **/ @RequestMapping("/upload") public ResultApi upload(@RequestParam("file") MultipartFile file) throws Exception { if (file.isEmpty()) { throw new RRException("上傳檔案不能為空"); } String url; //此處域名獲取可以根據自需求編寫 String domainUrl = OSSFactory.build().getDomainPath(); logger.info("配置的域名為"+domainUrl); if (StringUtils.isNotBlank(domainUrl)){ url = uploadFile(file,domainUrl); return ResultApi.success.put("url",url); }else { return ResultApi.error("域名配置為空,請先配置物件儲存域名"); } } /** * @Author maoqitian * @Description 上傳檔案到 FastDFS * @Date 2018/10/29 0029 11:11 * @Param [file] * @Param [domainName] 域名 * @return path 檔案訪問路徑 **/ public String uploadFile(MultipartFile file,String domainName) throws IOException { String[] fileAbsolutePath={}; String fileName=file.getOriginalFilename(); String ext=fileName.substring(fileName.lastIndexOf(".")+1); byte[] file_buff=null; InputStream inputStream = file.getInputStream(); if(inputStream!=null){ int available = inputStream.available(); file_buff=new byte[available]; inputStream.read(file_buff); } inputStream.close(); FastDFSFileEntity fastDFSFileEntity=new FastDFSFileEntity(fileName,file_buff,ext); try { fileAbsolutePath=FastDFSClient.getInstance().upload(fastDFSFileEntity); logger.info(fileAbsolutePath.toString()); }catch (Exception e){ logger.error("upload file Exception!",e); throw new RRException("檔案上傳出錯"+e); } if(fileAbsolutePath == null){ logger.error("upload file failed,please upload again!"); throw new RRException("檔案上傳失敗,請重新上傳"); } String path=domainName+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1]; return path; } 複製程式碼
複製程式碼
-
-
測試一下該方法,上傳一個圖片
-
由日誌列印我們可以看出圖片已經上傳成功
-
測試訪問上傳的圖片
-
到此,FastDFS伺服器叢集部署和整合客戶端到SpringBoot中已經完成,以後我們就可以愉快的使用FastDFS服務儲存我們的圖片等並備份。如果文章中有寫得不對的地方,請給我留言指出,大家一起學習進步。如果覺得我的文章給予你幫助,也請給我一個喜歡和關注。