关于分布式文件系统:分布式文件系统怎么做终于有个人把分布式文件上传讲清楚了

30次阅读

共计 10750 个字符,预计需要花费 27 分钟才能阅读完成。

FastDFS 概念

  • FastDFS 是开源的轻量级分布式文件系统, 实现 文件治理, 次要性能:

    • 文件存储
    • 文件同步
    • 文件拜访(文件上传, 文件下载)
  • 解决了大容量存储和负载平衡的问题, 特地适宜以文件为载体的在线服务: 相册网站, 视频网站
  • FastDFS 为互联网量身定制, 充分考虑了 冗余备份, 负载平衡, 线性扩容 等机制, 并重视高可用, 高性能等指标, 应用 FastDFS 能够很不便地搭建一套高性能的 文件服务器集群 提供文件上传, 下载等服务

    FastDFS 文件系统架构

  • FastDFS 服务端有两个角色:

    • 跟踪器(tracker): 次要做调度工作, 在拜访上起负载平衡作用

      • 跟踪器和存储节点都能够由一台服务器或多台服务器形成, 跟踪器和存储节点中的服务器能够随时减少或下线而不会影响下线服务.
      • 跟踪器中所有服务都是对等的, 能够依据服务器的压力状况随时减少或缩小
    • 存储节点(storage): 存储文件, 实现文件治理的所有性能

      • 就是这样的存储
      • 同步存储接口
      • 提供存储接口
      • FastDFS 同时对 文件 metadata进行治理, 文件 metadata 是文件属性列表, 能够蕴含多个键值对

        • 文件 metadata: 文件的相干属性, 以键值对形式示意
        • 为了反对大容量, 存储节点采纳分卷的组织形式

          • 存储系统由一个卷或多个卷组成, 卷与卷之间的文件是互相独立的, 所有卷的文件容量累加就是整个存储系统的文件容量
          • 一个卷能够由一台或多台存储服务器组成, 一个卷下的存储服务器中文件都是雷同的, 卷中的多台服务器起到了冗余备份和负载平衡作用
          • 在卷中减少服务器时, 同步已有的文件由零碎主动实现, 同步实现后, 零碎主动将新增服务器切换到线上提供服务
          • 当存储空间有余或行将耗尽时, 能够动静增加卷, 只须要减少一台或多台服务器, 配置一个新的卷, 这样扩充存储系统的容量
          • FastDFS 文件标识 分为两局部:

            • 卷名
            • 文件名

              高可用要有解体复原的能力
              服务集群要有同步的性能
              否则就要有负载平衡

              上传交互过程

  • client 询问 tracker 上传到的 storage, 不须要附加参数
  • tracker 返回一台可用的 storage
  • client 间接和 storage 通信实现文件上传

    client 为应用 FastDFS 的调用方,client 也是一台服务器, 对 tracker 和对 storage 的调用均为服务器间的调用

    下载交互过程

  • client 询问 tracker 下载文件的 storage, 参数为文件标识(卷名和文件名)
  • tracker 返回一台可用的 storage
  • client 间接和 storage 通信实现文件下载

    client 为应用 FastDFS 的调用方,client 也是一台服务器, 对 tracker 和对 storage 的调用均为服务器间的调用

    FastDFS 联合 Nginx

  • 应用 FastDFS 部署分布式文件系统时, 通过 FastDFS 的客户端 API 进行文件的上传, 下载, 删除等操作, 同时通过 FastDFS 和 HTTP 服务器来提供 HTTP 服务. 然而 FastDFS 的 HTTP 服务较为简单, 无奈提供负载平衡等高性能的服务. 须要应用 FastDFS 的 Nginx 模块补救这一缺点
  • FastDFS 通过 tracker 服务器, 将文件放在 storage 服务器存储, 然而同组之间的服务器须要复制文件, 有提早的问题, 能够通过 fastdfs-nginx-module 能够重定向连贯到源服务器取文件, 防止客户端因为复制提早的问题, 呈现谬误

    基于 Docker 装置 FastDFS

  • 环境筹备:

    • libfastcommon: FastDFS 拆散出的一些专用函数包
    • FastDFS: FastDFS 本体
    • fastdfs-nginx-module: FastDFS 和 nginx 的关联模块
    • nginx: nginx1.15.4
  • 创立工作目录:

    • 在 Linux 中创立

      /usr/local/docker/fastdfs/environment
      
      /usr/local/docker/fastdfs: 用于寄存 docker-compose.yml 配置文件及 FastDFS 数据卷
      /usr/local/docker/fastdfs/environment: 用于寄存 Dockerfile 镜像配置文件及 FastDFS 所需环境
  • 在 /usr/local/docker/fastdfs/environment 目录中创立 Dockerfile

    # 更新数据源
    WORKDIR /etc/apt
    RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse' > sources.list
    RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse' >> sources.list
    RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse' > sources.list
    RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse' > sources.list
    RUN apt-get update
    
    # 装置依赖
    RUN apt-get install make gcc libpcre3-dev zliblg-dev --assume-yes
    
    # 复制工具包
    ADD fastdfs-5.11.tar.gz /usr/local/src
    ADD fastdfs-nginx-module_v1.16.tar.gz /usr/local/src
    ADD libfastcommon.tar.gz /usr/local/src
    ADD nginx-1.15.4.tar.gz /usr/local/src
    
    # 装置 libfastcommon
    WORKDIR /usr/local/src/libfastcommon
    RUN ./make.sh && ./make.sh install
    
    # 装置 FastDFS
    WORKDIR /usr/local/src/fastdfs-5.11
    RUN ./make.sh && ./make.sh install
    
    # 配置 FastDFS tracker
    ADD tracker.conf /etc/fdfs
    RUN mkdir -p /fastdfs/tracker
    
    # 配置 FastDFS storage
    ADD storage.conf /etc/fdfs
    RUN mkdir -p /fastdfs/storage
    
    # 配置 FastDFS 客户端
    ADD client.conf /etc/fdfs
    
    # 配置 fastdfs-nginx-module
    ADD config /usr/local/src/fastdfs-nginx-modules/src
    
    # FastDFS 与 Nginx 集成
    WORKDIR /usr/local/src/nginx-1.13.6
    RUN ./configure --add-module=/usr/local/src/fastdfs-nginx-module/src
    RUN make && make install
    ADD mod_fastdfs.conf /etc/fdfs
    
    WORKDIR /usr/local/src/fastdfs-5.11/conf
    RUN cp http.conf mime.types /etc/fdfs/
    
    # 配置 Nginx
    ADD nginx.conf /usr/local/nginx/conf
    
    COPY entrypoint.sh /usr/local/bin/
    ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
    
    WORKDIR /
    EXPOSE 8888
    CMD ["/bin/bash"]
  • 在 /usr/local/docker/fastdfs/environment 创立 entrypoint.sh, 创立实现的后要执行 chmod +x entrypoint.sh 命令文件才能够应用

    # !/bin/sh
    /etc/init.d/fdfs_trackerd start
    /etc/init.d/fdfs_storaged start
    /usr/local/nginx/sbin/nginx -g 'daemon off;'

    相干配置文件

  • tracker.conf: FastDFS 跟踪器配置, 容器门路为:/etc/fdfs, 批改:

    base_path=/fastdfs/tracker
  • storage.conf: FastDFS 存储节点配置, 容器门路为:/etc/fdfs, 批改:

    base_path=/fastdfs/storage
    store_path0=/fastdfs/storage
    tracker_server=192.168.32.255:22122
    http.server_port=8888
  • client.conf: FastDFS 客户端配置, 容器中门路为:/etc/fdfs, 批改:

    base_path=/fastdfs/tracker
    tracker_server=192.168.32.255:22122
  • config: fastdfs-nginx-module 配置文件, 容器中门路为:/usr/local/src/fastdfs-nginx-module/src, 批改:

    # 批改前
    CORE_INCS="$CORE_INCS /usr/local/include/fastdfs /usr/local/include/fastcommon/"
    CORE_LIBS="$CORE_LIBS -L/usr/local/lib -lfastcommon -lfdfsclient"
    
    # 批改后
    CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/"
    CORE_LIBS="$CORE_LIBS -L/usr/lib -lfastcommon -lfdfsclient"
  • mod_fastdfs.conf: fastdfs-nginx-module 配置文件, 容器中门路:/usr/local/src/fastdfs-nginx-module/src, 批改:

    connect_timeout=10
    tracker_server=192.168.75.128:22122
    url_have_group_name=true
    store_path0=/fastdfs/storage
  • nginx.conf: Nginx 配置文件, 容器中的门路为:/usr/local/src/nginx-1.15.4/conf, 批改:

    user root;
    worker_processes 1;
    
    events {worker_connections 1024;}
    
    http{
      include                    mime.types;
      defaulte_type        application/octet-stream;
    
      sendfile                on;
    
      keepalive_timeout    65;
    
      server{
          listen                8888;
          server_name     localhost;
    
          location ~/group([0-9])/M00{ngx_fastddfs_module;}
    
          error_page    500 502 503 504 /50x.html
          location = /50x.html {root html;}
      }
    }

    启动容器

  • docker-compose.yml: 在 /usr/local/docker/fastdfs 文件夹中创立 docker-compose.yml

    version: '3.1'
    services:
     fastdfs:
    build: environment
    restart: always
    container_name: fastdfs
    volumes:
     - ./storage:/fastdfs/storage
    network_mode: host            # 网络模式: 主机模式 -- 将所有端口映射到主机,Docker 容器与宿主机共享端口, 即端口统一
  • 执行命令, 使文件编译失效

    docker-compose up -d

    测试上传

  • 交互式进入容器:

    docker exec -it fastdfs /bin/bash
  • 测试文件上传: 在 /usr/bin 目录中执行 ( 第 1 个 是二进制可执行文件客户端,第 2 个 是客户端的客户端配置文件,第 3 个 是须要上传的文件)

    /usr/bin/fdfs_upload_file /etc/fdfs/client.conf /usr/local/src/fastdfs-5.11/INSTALL
  • 服务器反馈上传地址: 文件的上传门路 (非地址), 通过在浏览器输出Ngnix 的拜访地址 + 文件上传门路 即可拜访服务器上的文件

    group1/M00/00/00/wKliyyfhHHkjsio986777
  • 测试 Nginx 拜访: 通过在浏览器输出 Ngnix 的拜访地址 + 文件上传门路 即可拜访服务器上的文件

    http://192.168.32.255:8888/group1/M00/00/00/wKliyyfhHHkjsio986777

    配置 FastDFS Java 客户端

  • 创立我的项目: 创立我的项目名为 myshop-service-upload 的服务提供者我的项目

    装置 FastDFS Java 客户端

  • 从 github 上 git clone FastDFS 我的项目代码:

    git clone https://github.com/happyfish100/fastdfs-client-java.git
  • 配置到本地仓库: 在我的项目目录的 target 包下有我的项目的 jar 文件

    mvn clean install
  • 将我的项目 jar 文件上传到 Nexus 中
  • 在我的项目中增加依赖:

    <!--FastDFS Begin-->
    <dependency>
      <groupId>org.csource</groupId>
      <artifactId>fastdfs-client-java</artifactId>
      <version>1.27-SNAPSHOT</version>
    </dependency>

    创立 FastDFS 工具类

  • 定义文件存储服务接口:

    package com.oxford.myshop.service.upload.fastdfs;
    
    public interface StorageService{
      /**
       * 上传文件
       *
       *@param data    文件的二进制符
       *@param extName 扩展名
       *@return          上传胜利后返回生成文件的 id, 失败则返回 null
       */
       public String upload(byte[] data,String extName);
       
       /**
       * 删除文件
       *
       *@param fileId    被删除的文件 id
       *@return            删除胜利后返回 0, 失败后返回错误代码
       */
       public int delete(String fileId);
    }
  • 实现文件存储服务接口:

    public class FastDFSStorageService implements StorageService,InitializingBean{private static final Logger logger=LoggerFactory.getLogger(FastDFSStorageService.class);
    
      private TrackerClient trackerClient;
    
      @Value("${storage.fastdfs.tracker_server}")
    
      @Override
      public String upload(byte[] data,String extName){
          TrackerServer trackerServer=null;
          StorageServer storageServer=null;
          StorageClient storageClient=null;
          try{NameValuePair[] meta_list=null;        // new NameValuePair[0]
    
              trackerServer=trackerClient.getConnection();
              if(trackerServer==null){logger.error("getConnection return null");
              }
              storageServer=trackerClient.getStoreStorage(trackerServer);
              storageClient1=new StorageClient1(trackerServer,storageServer);
              String fileId=storageClient1.upload_file1(data,extName,meta_list);
              logger.debug("uploaded file <{}>",fileId);
              return fileId;
          }catch(Exception ex){logger.error("Uploaded fail",ex);
              return null;
          }finally{if(storageServer!=null){
                  try{storageServer.close();
                  }catch(IOException e){e.printStackTrace();
                  }
              }
              if(trackeServer!=null){
                  try{trackeServer.close();
                  }catch(IOException e){e.printStackTrace();
                  }
              }
              storageClient1=null;
          }
      }
    
      @Override
      public int delete(String fileId){
          TrackerServer trackerServer=null;
          StorageServer storageServer=null;
          StorageClient storageClient=null;
          int index=fileId.indexOf('/');
          String groupName=fileId.substring(0,index);
          try{trackerServer=trackerClient.getConnection();
              if(trackerServer==null){logger.error("getConnection return null");
              }
              storageServer=trackerClient.getStoreStorage(trackerServer,groupName);
              storageClient1=new StorageClient1(trackerServer,storageServer);
              int result=storageClient1.delete_file1(fileId);
              return result;
          }catch(Exception ex){logger.error("Delete fail",ex);
              return 1;
          }finally{ifreturn fileId;}catch(Exception ex){logger.error("Uploaded fail",ex);
              return null;
          }finally{if(storageServer!=null){
                  try{storageServer.close();
                  }catch(IOException e){e.printStackTrace();
                  }
              }
              if(trackeServer!=null){
                  try{trackeServer.close();
                  }catch(IOException e){e.printStackTrace();
                  }
              }
              storageClient1=null;
          }
      }
      @Override
      public void afterPropertiesSet() throws Exxception{File confFile=File.createTempFile("fastdfs",".conf");
          PrintWriter confWriter=new PrintWriter(new FileWriter(confFile));
          confWriter.println("tracker_server="+trackerServer);
          confWriter.close();
          ClientGlobal.init(confFile.getAbsolutePath());
          confFile.delete();
          TrackerGroup trackerGroup=ClientGlobal.g_tracker_group;
          trackerClient=new TrackerClient(trackerGroup)
    
          logger.info("Init FastDFS with tracker_server : {}",trackerServer);
      }
    }
  • 文件存储服务工厂

    public class StorageFactory implements FactoryBean<StorageService>{
      @Autowired
      private AutowireCapableBeanFactory acbf;
    
      /**
       * 存储服务的类型, 仅反对 FastDFS
       */
       @Value("${storage.type}")
       private String type;
       
       private Map<String,Class<? extends StorageService>> classMap;
    
       public StorageFactory(){classMap=new HashMap<>();
          classMap.put("fastdfs",FastDFSStorageService.class);
      } 
    
      @Override
      public StorageService getObject() throws Exception{Class<? extends StorageService> clazz=classMap.get(type);
          if(class==null){throw new RuntimeException("Unsupported storage type ["+type+"],valid are"+ classMap.keySet());
          }
    
          StorageService bean=clazz.newInstance();
          acbf.autowireBean(bean);
          acbf.initializeBean(bean,bean.getClass().getSimpleName());
          return bean;
      }
    
      @Override
      public Class<?> getObjectType(){return StorageService.class;}
    
      @Override
      public boolean isSingleton(){return true;}
    }
  • 配置文件存储服务工厂类

    /**
     * Java 配置形式定义 StorageFactory 的 bean, 使其能够被依赖注入
     */
     @Configuration
     public classs FastDFSConfiguration{
       @Bean
       public StorageFactory storageFactory(){return new StorageFactory();
       }
     }

    创立 FastDFS 控制器

  • 减少云配置: application.yml

    # SpringBoot Application
    spring:
     application:
    name: myshop-service-upload
    
    # FastDFS Configuration
    fastdfs.base.url: htttp//192.168.32.255:8888/
    storage:
     type: fastdfs
     fastdfs:
    tracker_server: 192.168.32.255:22122
  • 控制器代码

    @CrossOrigin(origins="*",maxAge=3600)
    @RestController
    public class UploadController{@Value("${fastdfs.base.url}")
      private String FASTDFS_BASE_URL;
    
      @Autowired
      private StorageService storageService;
    
      @RequestMapping(value="upload",method=RequestMethod.POST)
      public Map<String,Object> upload(MultipartFile dropFile,MultipartFile[] editorFiles){Map<String,Object> result=new HashMap<>();
    
          //DropZone 上传
          if(dropFile!=null){result.put("fileName",writeFile(dropFile));
          }
    
          //wangEditor 上传
          if(editorFiles != null && editorFiles.length > 0){List<String> fileNames=new ArrayList<>();
    
              for(MultipartFile editorFile:editorFiles){fileNames.add(writeFile(editorFile));
              }
    
              result.put("error",0);
              result.put("data",fileNames);
          }
          return result;
      }
      
      /**
       * 将图片写入指定目录
       */
       private String writeFile(MultipartFile multipartFile){
          // 获取文件后缀
          String oName=multipartFile.getOriginalFilename();
          String exName=oName.substring(oName.lastIndexOf(".")+1);
          
          // 文件寄存门路
          String url=null;
          try{String uploadUrl=storageService.upload(multipartFile.getBytes(),exName);
              url=FASTDFS_BASE_URL+uploadUrl;
          }catch(IOException e){e.printStackTrace();
          }
          
          // 返回文件残缺门路
          return url;
      }
    }
  • 创立 SpringBoot Application, 运行执行分布式文件上传我的项目

正文完
 0