FastDFS伺服器叢集部署和整合客戶端到SpringBoot

maoqitian發表於2019-03-02

FastDFS是一個開源的輕量級分散式檔案系統,它對檔案進行管理,功能包括:檔案儲存、檔案同步、檔案訪問(檔案上傳、檔案下載)等,解決了大容量儲存和負載均衡的問題,同時也能做到在叢集環境下一臺機子上傳檔案,同時該組下的其他節點下也備份了上傳的檔案。做分散式系統開發時,其中要解決的一個問題就是圖片、音視訊、檔案共享的問題和資料備份,分散式檔案系統正好可以解決這個需求。FastDFS的服務主要有兩個角色Tracker和Storage,Tracker服務用於負責排程storage節點與client通訊,在訪問上起負載均衡的作用,和記錄storage節點的執行狀態,是連線client和storage節點的樞紐,Storage用於儲存檔案

  • FastDFS叢集部署

    • 整體部署模組圖

      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

      • 下載libfatscommon

      • 解壓、安裝

        unzip libfastcommon-master.zip
        cd libfastcommon-master
        ./make.sh && ./make.sh install #編譯安裝
        複製程式碼
    • 安裝FastDFS

      • 下載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訪問使用
        複製程式碼

      etc目錄下fdfs目錄

    • 安裝fastdfs-nginx-module

      • 下載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

      • 下載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 #儲存日誌和資料的根目錄
      複製程式碼

      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
      複製程式碼

      檢測叢集1

      檢測叢集2

    • 圖片上傳測試

      #上傳成功返回 檔案訪問 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
      
      複製程式碼

      編譯打包FastDFS-java-client

      fastdfs-client-java打包成功

    • 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-java-client 上傳圖片成功

    • 測試訪問上傳的圖片

      測試上傳的圖片是否可以進行訪問

到此,FastDFS伺服器叢集部署和整合客戶端到SpringBoot中已經完成,以後我們就可以愉快的使用FastDFS服務儲存我們的圖片等並備份。如果文章中有寫得不對的地方,請給我留言指出,大家一起學習進步。如果覺得我的文章給予你幫助,也請給我一個喜歡和關注。

相關文章