写点什么

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

发布于: 2021 年 05 月 21 日
分布式文件系统怎么做?终于有个人把分布式文件上传讲清楚了

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/aptRUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse' > sources.listRUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse' >> sources.listRUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse' > sources.listRUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse' > sources.listRUN apt-get update
# 安装依赖RUN apt-get install make gcc libpcre3-dev zliblg-dev --assume-yes
# 复制工具包ADD fastdfs-5.11.tar.gz /usr/local/srcADD fastdfs-nginx-module_v1.16.tar.gz /usr/local/srcADD libfastcommon.tar.gz /usr/local/srcADD nginx-1.15.4.tar.gz /usr/local/src
# 安装libfastcommonWORKDIR /usr/local/src/libfastcommonRUN ./make.sh && ./make.sh install
# 安装 FastDFSWORKDIR /usr/local/src/fastdfs-5.11RUN ./make.sh && ./make.sh install
# 配置FastDFS trackerADD tracker.conf /etc/fdfsRUN mkdir -p /fastdfs/tracker
# 配置FastDFS storageADD storage.conf /etc/fdfsRUN mkdir -p /fastdfs/storage
# 配置FastDFS客户端ADD client.conf /etc/fdfs
# 配置fastdfs-nginx-moduleADD config /usr/local/src/fastdfs-nginx-modules/src
# FastDFS与Nginx集成WORKDIR /usr/local/src/nginx-1.13.6RUN ./configure --add-module=/usr/local/src/fastdfs-nginx-module/srcRUN make && make installADD mod_fastdfs.conf /etc/fdfs
WORKDIR /usr/local/src/fastdfs-5.11/confRUN cp http.conf mime.types /etc/fdfs/
# 配置NginxADD nginx.conf /usr/local/nginx/conf
COPY entrypoint.sh /usr/local/bin/ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
WORKDIR /EXPOSE 8888CMD ["/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/storagestore_path0=/fastdfs/storagetracker_server=192.168.32.255:22122http.server_port=8888
复制代码


  • client.conf: FastDFS 客户端配置,容器中路径为:/etc/fdfs,修改:


base_path=/fastdfs/trackertracker_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=10tracker_server=192.168.75.128:22122url_have_group_name=truestore_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 Applicationspring: application:  name: myshop-service-upload
# FastDFS Configurationfastdfs.base.url: htttp//192.168.32.255:8888/storage: type: fastdfs fastdfs: tracker_server: 192.168.32.255:22122
复制代码


  • 控制器代码


@CrossOrigin(origins="*",maxAge=3600)@RestControllerpublic 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,运行执行分布式文件上传项目

发布于: 2021 年 05 月 21 日阅读数: 1074
用户头像

一位攻城狮的自我修养 2021.04.06 加入

分享技术干货,面试题和攻城狮故事。 你的关注支持是我持续进步的最大动力! https://github.com/ChovaVea

评论

发布
暂无评论
分布式文件系统怎么做?终于有个人把分布式文件上传讲清楚了