theme: hydrogen
highlight: agate
头图来自:https://www.zcool.com.cn/u/14...
FastDFS
一、FastDFS根本理解
1、FastDFS概念
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行治理,性能包含:文件存储、文件同步、文件拜访(文件上传、文件下载)等,解决了大容量存储和负载平衡的问题。特地适宜以文件为载体的在线服务,如相册网站、视频网站等等。FastDFS为互联网量身定制,充分考虑了冗余备份、负载平衡、在线扩容等机制,并重视高可用、高性能等指标,应用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
2、FastDFS的作用
适宜用来存储用户图片、视频、文档等文件。
出于简洁思考,FastDFS没有对文件做分块存储,因而不太适宜分布式计算场景。
3、FastDFS的优缺点
长处:
- 适宜中小文件存储(倡议范畴:
4KB<file_size<500MB
) - 主备Tracker服务,加强零碎的可用性
- 零碎不须要反对POSIX,这样的话就升高了零碎的复杂度,使得解决的速度会更高
- 反对主从文件,反对自定义扩展名
- 反对在线扩容机制,加强了零碎的可扩展性
- 实现了软RAID,加强了零碎的并发解决能力和数据容错恢复能力
毛病:
- 间接按文件存储,可间接读取文件内容,不足文件安全性
- 通过API下载,存在单点的性能瓶颈
- 不反对断点续传,对大文件将是噩梦
- 同步机制不反对文件正确性校验,升高了零碎的可用性
- 不反对POSIX通用接口拜访,通用性比拟的低
- 对跨公网的文件同步,存在着比拟大的提早,须要利用做相应的容错策略
4、相干概念
FastDFS服务端有三个角色:跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)。tracker server:跟踪服务器,次要做调度工作,起负载平衡的作用。Tracker是FastDFS的协调者,负责管理所有的storage server和group,每个storage在启动后会连贯Tracker,告知本人所属的group等信息,并放弃周期性的心跳,tracker依据storage的心跳信息,建设group==>[storage server list]的映射表。
storage server:存储服务器(又称:存储节点或数据服务器),文件和文件属性(meta data)都保留到存储服务器上。Storage server间接利用OS的文件系统调用管理文件。
client:客户端,作为业务申请的发起方,通过专有接口,应用TCP/IP协定与跟踪器服务器或存储节点进行数据交互。FastDFS向使用者提供根本文件拜访接口,比方upload、download、append、delete等,以客户端库的形式提供给用户应用。
二、FastDFS原理
1、FastDFS零碎结构图
- FastDFS分为Tracker、Storage,其中Storage负责存储文件,Tracker负责存储文件所在地址,次要作用是负载平衡和资源调度。
- Tracker、Storage都能够实现集群部署,Tracker的每个节点位置平等,而Storage能够分为多个组,每个组之间保留的文件是不同的,组外部分为多个成员,每个成员保留的内容是一样,组成员位置统一,没有主从概念。
- 应用FastDFS存储文件长处:能够应答互联网的海量文件存储,一旦文件较多,能够随时横向扩大,且集群的实现也使零碎不存在单点故障问题,用户不会因为服务器宕机而无法访问文件资源。
2、FastDFS工作流程
文件上传
• 1. client询问tracker上传到的storage,不须要附加参数;
• 2. tracker返回一台可用的storage;
• 3. client间接和storage通信实现文件上传。
文件下载
- client询问tracker下载文件的storage,参数为文件标识(组名和文件名);
- tracker返回一台可用的storage;
- client间接和storage通信实现文件下载。
三、Linux下部署FastDFS
1、筹备工作
装置gcc(编译时须要)
yum install -y gcc gcc-c++
装置libevent(运行)
yum -y install libevent
创立文件夹并上传文件
mkdir -p /fileservice/fastcd /fileservice/fast
留神:须要将相干文件上传值该文件夹中。
2、装置libfastcommon
# 解压文件tar -zxvf libfastcommon-1.0.35.tar.gz# 进入目录cd libfastcommon-1.0.35# 编译./make.sh# 装置./make.sh install
应用make
指令
装置实现后
目录构造
3、装置fastdfs
装置相干依赖
yum install perl pcre pcre-devel zlib zlib-devel openssl openssl-devel -y
装置fastdfs
# 解压fastdfstar zxvf fastdfs-5.11.tar.gz# 进入目录cd fastdfs-5.11# 执行编译./make.sh# 装置./make.sh install
装置胜利之后
查看tracker和storage的可执行脚本
ll /etc/init.d/ | grep fdfs
筹备配置文件
默认在/etc/fdfs/上面
cd /etc/fdfs/
复制配置文件
cp client.conf.sample client.confcp storage.conf.sample storage.confcp storage_ids.conf.sample storage_ids.confcp tracker.conf.sample tracker.conf
创立批改tracker默认的存放数据和日志的须要的目录
mkdir -p /home/fastdfs/tracker
创立批改storage默认的存放数据和日志的须要的目录
mkdir -p /home/fastdfs/storage
配置和启动tracker
配置
# 切换到/etc/fdfs/下cd /etc/fdfs/# 批改tracker.confvim tracker.conf
批改默认的base_path
为新创建的文件,即/home/fastdfs/tracker
文件tracker.conf
的批改内容
# 批改默认的base_path,留神:根文件夹必须存在,子文件夹会主动创立base_path=/home/fastdfs/tracker
启动tracker
service fdfs_trackerd start
留神:服务启动胜利后,会在base_path
指定的目录下生成data
和logs
。
配置和启动storage
配置
# 切换目录cd /etc/fdfs/# 批改storage.conf vim storage.conf
文件tracker.conf
的配置
# 配置组名(可选)group_name=group1 # 配置base_path,留神:根文件夹必须存在,子文件夹会主动创立base_path=/home/fastdfs/storage#store寄存文件的地位(store_path), store_path0=/home/fastdfs/storage# 留神:如果挂载多个磁盘,如下配置# store_path1=.....# store_path2=......#。。。#配置tracker服务器:IPtracker_server=192.168.10.100:22122#如果有多个则配置多个tracker#tracker_server= xxx.xxx.xxx.xxx:22122#。。。
启动storage
service fdfs_storaged start
启动实现后进入 /home/fastdfs/storage/data
目录下,显示目录如下:
cd /home/fastdfs/storage/data/
4、应用FastDFS自带工具测试
配置
# 切换目录到cd /etc/fdfs/# 批改client.confvim client.conf
批改根本门路和tracker_server如下
# storage的根目录base_path=/home/fastdfs/storage# tracker的ip和地址tracker_server=192.168.10.100:22122# 留神:若tracker有多个,能够配置多个# tracker_server= ...# tracker_server= ...
上传一张图片到Linux的根目录/root/
进行测试
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /root/image-20200708110242109.jpg
如此,示意FastDFS搭建胜利。
上图中文件的地址:http://192.168.10.100:80/group1/M00/00/00/wKgKZF8FdfiAIvwVAAETxoCDaLI533.jpg,对应storage服务器上的 /home/fastdfs/storage/data/00/00/wKgKZF8FdfiAIvwVAAETxoCDaLI533.jpg文件
留神:此时该图片无奈应用http拜访下载,能够应用内置Web Server,也能够和其余Web Server配合应用
5、FastDFS为什么要联合Nginx?
通过FastDFS的HTTP服务器来提供HTTP服务。然而FastDFS的HTTP服务无奈提供负载平衡等高性能的服务,所以FastDFS的开发者—淘宝的架构师余庆为咱们提供了Nginx上应用的FastDFS模块(FastDFS的Nginx模块)。详情。
6、装置fastdfs-nginx-module
配置
# 解压 fastdfs-nginx-moduletar -zxvf fastdfs-nginx-module-1.20.tar.gz# 切换目录cd fastdfs-nginx-module-1.20/src# 批改configvim config
批改内容
ngx_module_incs="/usr/include/fastdfs /usr/include/fastcommon/"CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/"
拷贝配置mod_fastdfs.conf
并批改
# 将fastdfs-nginx-module/src下的mod_fastdfs.conf拷贝至/etc/fdfs/下cp mod_fastdfs.conf /etc/fdfs/# 批改 /etc/fdfs/mod_fastdfs.conf 的内容vim /etc/fdfs/mod_fastdfs.conf
批改内容如下:
# 配置跟踪服务器tracker的ip和端口tracker_server=192.168.10.100:22122 #url中蕴含group名称url_have_group_name=true#指定文件存储门路(为配置的store门路)store_path0=/home/fdfs_storage
拷贝配置文件
cp http.conf mime.types /etc/fdfs/
7、nginx的装置
配置
cd /fileservice/fast/# 解压nginxtar -zxvf nginx-1.15.2.tar.gz # 进入nginx解压的目录下cd nginx-1.15.2/# 退出模块命令配置./configure --prefix=/opt/nginx --sbin-path=/usr/bin/nginx --add-module=/fileservice/fast/fastdfs-nginx-module-1.20/src# 编译并装置make && make install# 批改nginx配置cd /opt/nginx/confvim nginx.conf
批改nginx配置的内容
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { # root html; # index index.html index.htm; ngx_fastdfs_module; }
启动ngnix
cd /usr/bin/# 启动./nginx
四、Docker搭建FastDFS
1、拉取镜像并启动
# 启动dockersystemctl start docker# -v ${HOME}/fastdfs:/var/local/fdfs是指:将${HOME}/fastdfs这个目录挂载到容器里的/var/local/fdfs这个目录里。#所以上传的文件将被长久化到${HOME}/fastdfs/storage/data里#IP 前面是本人的服务器公网ip或者虚拟机ip,#-e WEB_PORT=80 指定nginx默认端口docker run -d --restart=always --privileged=true --net=host --name=fastdfs -e IP=192.168.10.100 -e WEB_PORT=80 -v ${HOME}/fastdfs:/var/local/fdfs registry.cn-beijing.aliyuncs.com/tianzuo/fastdfs
2、测试上传
# 进入容器docker exec -it fastdfs /bin/bash# 创立文件echo "Hello FastDFS!">index.html# 测试文件上传fdfs_test /etc/fdfs/client.conf upload index.html
3、测试拜访
五、Java实现文件上传
1、创立Maven我的项目
2、引入依赖
<!-- fastdfs客户端jar --><dependency> <groupId>net.oschina.zcx7878</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27.0.0</version></dependency><!-- spring-core --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.25.RELEASE</version></dependency>
3、创立fdfs_client.conf
配置文件
# 连贯超时工夫connect_timeout=30# 网络超时工夫network_timeout=60# tracker根目录base_path=/home/fastdfs/tracker#改为本人服务器的iptracker_server=192.168.10.100:22122log_level=infouse_connection_pool = falseconnection_pool_max_idle_time = 3600load_fdfs_parameters_from_tracker=falseuse_storage_id = falsestorage_ids_filename = storage_ids.confhttp.tracker_server_port=80
4、编写测试类
public static void main(String[] args) throws Exception { // 文件的门路 String testFilePath = "C:/Users/hxxiapgy/Desktop/fastdfs架构.png"; // 获取fdfs_client.conf的 String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath(); // 1.加载配置文件 ClientGlobal.init(filePath); // 2.创立一个 TrackerClient 对象 TrackerClient trackerClient = new TrackerClient(); // 3.应用 TrackerClient 对象创立连贯,取得一个 TrackerServer 对象。 TrackerServer trackerServer = trackerClient.getConnection(); // 4.申明 StorageServer StorageServer storageServer = null; // 5.创立一个 StorageClient 对象,须要两个参数 TrackerServer 对象、StorageServer 的援用 StorageClient storageClient = new StorageClient(trackerServer, storageServer); // 6.通过storangeClient上传文件,返回组名和文件名 String[] strings = storageClient.upload_file(testFilePath, "png", null); // 打印组名与文件名 for (String string : strings) { System.out.println(string); } System.out.println("文件上传胜利!");}
六、SpringBoot上传文件
1、形式一:
创立我的项目
引入依赖
<!-- fastdfs依赖 --><dependency> <groupId>net.oschina.zcx7878</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27.0.0</version></dependency><!-- lang3 --><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId></dependency>
批改配置文件
fastdfs: connect_timeout_in_seconds: 120 network_timeout_in_seconds: 120 charset: UTF-8 tracker_servers: 192.168.10.100:22122
编写UploadService
/** * @description: 文件上传 * @author: Hxxiapgy * @date: 2020/7/9 20:02 */@Servicepublic class UploadService { @Value("${fastdfs.connect_timeout_in_seconds}") private Integer connectTimeout; @Value("${fastdfs.network_timeout_in_seconds}") private Integer networkTimeout; @Value("${fastdfs.charset}") private String charset; @Value("${fastdfs.tracker_servers}") private String trackerServers; public Map<String,Object> upload(MultipartFile multipartFile){ // 判断文件是否存在 if(multipartFile == null){ throw new RuntimeException("文件不能为空!"); } // 将文件上传到fastdfs String fileId = fdfsUpload(multipartFile); // 判断fileId是否为空 if (fileId == null){ throw new RuntimeException("文件上传失败!"); } Map<String,Object> map = new HashMap<>(); map.put("code", 1); map.put("msg", "文件上传胜利"); map.put("fileId", fileId); return map; } /** * 性能形容: * @param * @return {@link String} * @author hxxiapgy * @data 2020/7/9 20:08 */ private String fdfsUpload(MultipartFile multipartFile){ // 1.初始化FastDFS的环境 initFdfsConfid(); // 1.创立trackerClient对象 TrackerClient trackerClient = new TrackerClient(); try { // 2.获取trackerService TrackerServer trackerServer = trackerClient.getConnection(); // 3.申明StorageService StorageServer storageServer = null; // 4.获取StorageClient StorageClient1 storageClient1 = new StorageClient1(trackerServer,storageServer); // 5.获取文件后缀 String originalFilename = multipartFile.getOriginalFilename(); // 文件异样 if (StringUtils.isBlank(originalFilename)) { throw new RuntimeException("文件读取异样!"); } String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); // 6.通过StorageService上传文件,返回组名和文件名 String fileId = storageClient1.upload_file1(multipartFile.getBytes(), extName , null); return fileId; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 性能形容: 加载fastDFS环境 * @param * @return * @author hxxiapgy * @data 2020/7/9 20:35 */ private void initFdfsConfid() { try { // 设置连贯超时工夫 ClientGlobal.setG_connect_timeout(connectTimeout); // 设置网络超时工夫 ClientGlobal.setG_network_timeout(networkTimeout); // 设置字符编码 ClientGlobal.setG_charset(charset); // 设置trackerService ClientGlobal.initByTrackers(trackerServers); } catch (Exception e) { e.printStackTrace(); } }}
编写UploadController
/** * @description: 文件上传 * @author: Hxxiapgy * @date: 2020/7/9 20:40 */@Controllerpublic class UploadController { @Resource private UploadService uploadService; @PostMapping("/upload") @ResponseBody public Map<String,Object> upload(MultipartFile multipartFile){ Map<String, Object> map = uploadService.upload(multipartFile); return map; } @RequestMapping("/index") public String index(){ return "index.html"; }}
编写测试入口index.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>测试文件上传</title></head><body> <h3>文件上传</h3> <hr/> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="multipartFile"/> <input type="submit" value="上传"/> </form></body></html>
测试
形式二
创立我的项目
引入依赖
<!-- fastdfs client --><dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>1.26.7</version></dependency>
批改配置文件
fdfs: so-timeout: 2500 # 读取工夫 connect-timeout: 600 # 连贯超时工夫 thumb-image: # 缩略图 width: 100 height: 100 tracker-list: # tracker服务配置地址列表 - 192.168.10.100:22122upload: base-url: http://192.168.10.100/ allow-types: - image/jpeg - image/png - image/bmp - image/gif
编写fastdfs的属性类
/** * @description: 配置fastdfs的属性类 * @author: Hxxiapgy * @date: 2020/7/9 21:58 */@ConfigurationProperties(prefix = "upload")@Datapublic class UploadProperties { private String baseUrl; private List<String> allowTypes; public String getBaseUrl() { return baseUrl; } public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; } public List<String> getAllowTypes() { return allowTypes; } public void setAllowTypes(List<String> allowTypes) { this.allowTypes = allowTypes; }}
编写UploadService
/** * @description: 文件上传 * @author: Hxxiapgy * @date: 2020/7/9 22:01 */@Service@EnableConfigurationProperties(UploadProperties.class)public class UploadService { private Log log= LogFactory.getLog(UploadService.class); @Resource private FastFileStorageClient storageClient; @Resource private UploadProperties uploadProperties; /** * 性能形容: 文件上传 * @param multipartFile 要上传的文件 * @return {@link String} 返回文件门路 * @author hxxiapgy * @data 2020/7/9 22:05 */ public String upload(MultipartFile multipartFile){ // 1.校验上传文件是否为空 if (multipartFile == null){ log.error("文件不存在"); throw new RuntimeException("文件为空!"); } // 1.校验文件类型 //获取上传文件的类型 String contentType = multipartFile.getContentType(); if (!uploadProperties.getAllowTypes().contains(contentType)){ log.info("文件类型不反对!"); throw new RuntimeException("文件类型不反对!"); } try { // 2.校验文件内容 // 读取文件 BufferedImage bufferedImage = ImageIO.read(multipartFile.getInputStream()); if (bufferedImage == null || bufferedImage.getWidth() == 0 || bufferedImage.getHeight() == 0){ log.error("上传文件有问题"); throw new RuntimeException("上传文件有问题!"); } } catch (IOException e) { log.error("测验文件内容失败...{}",e); throw new RuntimeException("测验文件内容失败...{}" + e.getMessage()); } // 3.获取扩展名 String extension = StringUtils.substringAfterLast(multipartFile.getOriginalFilename(),"."); try { // 4.上传文件 StorePath storePath = storageClient.uploadFile(multipartFile.getInputStream(), multipartFile.getSize(), extension, null); // 5.返回门路 return uploadProperties.getBaseUrl() + storePath.getFullPath(); } catch (Exception e) { log.error("文件上传失败!...{}",e); throw new RuntimeException("文件上传失败...{}" + e.getMessage()); } }}
编写UploadController
/** * @description: 文件上传 * @author: Hxxiapgy * @date: 2020/7/9 22:25 */@Controllerpublic class UploadController { @Resource private UploadService uploadService; /** * 性能形容: 文件上传 * @param multipartFile 要删除的文件 * @return {@link Map< String, Object>} * @author hxxiapgy * @data 2020/7/9 22:27 */ @PostMapping("/upload") @ResponseBody public Map<String,Object> upload(MultipartFile multipartFile){ String filePath = uploadService.upload(multipartFile); Map<String,Object> map = new HashMap<>(); map.put("code","200"); map.put("msg", "文件上传胜利"); map.put("filePath", filePath); return map; }}
编写测试入口
同办法一。