分布式文件系统fastDFS研究
什么是分布式文件系统
技术应用场景
一个网站拥有大量的视频和图片资源,并且免费提供用户去下载,文件太多如何高效存储?用户访问量大,如何提升访问速度?
分布式文件系统可以解决上述问题.
分布式文件系统解决了海量文件存储及传输访问的瓶颈问题,对海量视频的管理、对海量图片的管理等。
什么是文件系统
总结:文件系统是负责管理和存储文件的系统软件,它是操作系统和硬件驱动之间的桥梁,操作系统通过文件系统
提供的接口去存取文件,用户通过操作系统访问磁盘上的文件。如下图:
常见的文件系统:FAT16/FAT32、NTFS、HFS、UFS、APFS、XFS、Ext4等 。
什么是分布式文件系统
为什么会有分布文件系统呢?
分布式文件系统是面对互联网的需求而产生,互联网时代对海量数据如何存储?靠简单的增加硬盘的个数已经满足
不了我们的要求,因为硬盘传输速度有限但是数据在急剧增长,另外我们还要要做好数据备份、数据安全等。
采用分布式文件系统可以将多个地点的文件系统通过网络连接起来,组成一个文件系统网络,结点之间通过网络进
行通信,一台文件系统的存储和传输能力有限,我们让文件在多台计算机上存储,通过多台计算共同传输。如下
图:
好处:
1、一台计算机的文件系统处理能力扩充到多台计算机同时处理。
2、一台计算机挂了还有另外副本计算机提供数据。
3、每台计算机可以放在不同的地域,这样用户就可以就近访问,提高访问速度。
主流的分布式文件系统
1、NFS
2、GFS
1)GFS采用主从结构,一个GFS集群由一个master和大量的chunkserver组成。
2)master存储了数据文件的元数据,一个文件被分成了若干块存储在多个chunkserver中。
3)用户从master中获取数据元信息,从chunkserver存储数据。
3、HDFS
1)HDFS采用主从结构,一个HDFS集群由一个名称结点和若干数据结点组成。
名称结点存储数据的元信息,一个完整的数据文件分成若干块存储在数据结点。
2)客户端从名称结点获取数据的元信息及数据分块的信息,得到信息客户端即可从数据块来存取数据。
分布式文件服务提供商
什么是fastDFS
fastDSF介绍
FastDFS是用c语言编写的一款开源的分布式文件系统,它是由淘宝资深架构师余庆编写并开源。FastDFS专为互联
网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很
容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
为什么要使用fastDFS呢?
上边介绍的NFS、GFS都是通用的分布式文件系统,通用的分布式文件系统的优点的是开发体验好,但是系统复杂
性高、性能一般,而专用的分布式文件系统虽然开发体验性差,但是系统复杂性低并且性能高。fastDFS非常适合
存储图片等那些小文件,fastDFS不对文件进行分块,所以它就没有分块合并的开销,fastDFS网络通信采用
socket,通信速度很快。
fastDSF工作原理
fastDSF架构
FastDFS架构包括 Tracker server和Storageserver。客户端请求Tracker server进行文件上传、下载,通过Tracker
server调度最终由Storage server完成文件上传和下载。
1)Tracker
Tracker Server作用是负载均衡和调度,通过Tracker server在文件上传时可以根据一些策略找到Storage server提
供文件上传服务。可以将tracker称为追踪服务器或调度服务器。
FastDFS集群中的Tracker server可以有多台,Tracker server之间是相互平等关系同时提供服务,Tracker server
不存在单点故障。客户端请求Tracker server采用轮询方式,如果请求的tracker无法提供服务则换另一个tracker。
2)Storage
Storage Server作用是文件存储,客户端上传的文件最终存储在Storage服务器上,Storage server没有实现自己
的文件系统而是使用操作系统的文件系统来管理文件。可以将storage称为存储服务器。
3)Storage状态收集
Storage server会连接集群中所有的Tracker server,定时向他们报告自己的状态,包括磁盘剩余空间、文件同步
状况、文件上传下载次数等统计信息。
总结:
(1)客户端请求tracker执行下载或者上传文件,tracker负责找到文件在storage集群的位置,将信息返回客户端
(2)客户端拿到信息,向storage具体组,和具体文件位置执行操作
(3)storage可以在配置时分多个group组,每组可以有多台storage服务器,但是每台服务器,再执行上传或者下载之后都会将
数据同步到该组下的其他服务器上,因此一个组的每个服务器文件内容一样,这样是为了高可用
文件上传流程
客户端上传文件后存储服务器将文件ID返回给客户端,此文件ID用于以后访问该文件的索引信息。文件索引信息
包括:组名,虚拟磁盘路径,数据两级目录,文件名。
组名:文件上传后所在的storage组名称,在文件上传成功后有storage服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0则是M00,
如果配置了store_path1则是M01,以此类推。
数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创
建时间戳、文件大小、随机数和文件拓展名等信息。
文件下载流程
tracker根据请求的文件路径即文件ID 来快速定义文件。
文件id从数据库中获取到,再去文件系统中查找
比如请求下边的文件:
1.通过组名tracker能够很快的定位到客户端需要访问的存储服务器组是group1,并选择合适的存储服务器提供客 户端访问。
2.存储服务器根据“文件存储虚拟磁盘路径”和“数据文件两级目录”可以很快定位到文件所在目录,并根据文件名找到 客户端需要访问的文件。
fastDFS入门
FastDFS 安装与配置
导入虚拟机
1、使用Vmware打开虚拟机配置文件“CentOS 7 64 位.vmx”,提示如下图:
2、选择“我已复制该虚拟机”
3、启动虚拟机之前启动VMware的服务:
fastDFS安装
分别在 192.168.101.3 和 192.168.101.4 上安装 tracker。
注:初次安装可只安装一台 tracker,快速体验。
tracker 和 storage 使用相同的安装包,下载地址:
https://github.com/happyfish100/FastDFS
本测试下载:FastDFS_v5.05.tar.gz
FastDFS 安装环境
FastDFS 是 C 语言开发,建议在 linux 上运行,本教程使用 Centos7 作为安
装环境。
安装 FastDFS 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果
没有 gcc 环境,需要安装 gcc:yum install gcc-c++
安装 libevent
FastDFS 依赖 libevent 库,需要安装:
yum -y install libevent
安装 libfastcommon
libfastcommon 是 FastDFS 官方提供的,libfastcommon 包含了 FastDFS 运行所需
要的一些基础库。
将 libfastcommonV1.0.7.tar.gz 拷贝至/usr/local/下
cd /usr/local
tar -zxvf libfastcommonV1.0.7.tar.gz
cd libfastcommon-1.0.7
./make.sh
./make.sh install
注意:make指令不能用的话是缺少相关指令包,下载即可
注意:libfastcommon 安装好后会自动将库文件拷贝至/usr/lib64 下,由于 FastDFS 程
序引用 usr/lib 目录所以需要将/usr/lib64 下的库文件拷贝至/usr/lib 下。
要拷贝的文件如下:
tracker 编译安装
将 FastDFS_v5.05.tar.gz 拷贝至/usr/local/下
tar -zxvf FastDFS_v5.05.tar.gz
cd FastDFS
./make.sh 编译
./make.sh install 安装
安装成功将安装目录下的 conf 下的文件拷贝到/etc/fdfs/下。
配置
安装成功后进入/etc/fdfs 目录:
拷贝一份新的 tracker 配置文件:
cp tracker.conf.sample tracker.conf
修改 tracker.conf
vi tracker.conf
端口:port=22122
存储策略:store_lookup= 取值范围:0(轮询向storage存储文件)、1(指定具体的group)、2负载均衡,选择空闲的storage存储
指定具体的group:store_group= 如果store_lookup设置为1则这里必须指定一个具体的group。
tracker 基础目录:base_path=/home/fastdfs,tracker在运行时会向此目录存储storage的管理数据。
启动tracker
usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart
注意:没有目录自行创建,进程占用就kill掉
storage 安装
分别在 192.168.101.5、192.168.101.6、192.168.101.7、192.168.101.8 上安装 storage。
注:初次安装可只安装一台 storage,快速体验。
安装 libevent
同 tracker 安装
安装 libfastcommon
同 tracker 安装。
storage 编译安装
同 tracker 编译安装。
配置
安装成功后进入/etc/fdfs 目录:
拷贝一份新的 storage 配置文件:
cp storage.conf.sample storage.conf
修改 storage.conf
vi storage.conf
group_name=group1
向tracker心跳间隔(秒):heart_beat_interval=30
storage基础目录:base_path=/home/fastdfs
磁盘存储目录,可定义多个store_path:
store_path0=/home/fastdfs/fdfs_storage 此目录下存储上传的文件,在/home/fastdfs/fdfs_storage/data下 store_path1=... ...
上报tracker的地址:tracker_server=192.168.101.64:22122
如果有多个tracker则配置多个tracker,比如:
tracker_server=192.168.101.64:22122
tracker_server=192.168.101.65:22122
....
启动storage
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart
文件上传下载测试
搭建环境
这里我们使用javaApi测试文件的上传,java版本的fastdfs-client地址在:
https://github.com/happyfifish100/fastdfs-client-java,参考此工程编写测试用例。
1)创建maven工程
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xc-framework-parent</artifactId>
<groupId>com.xuecheng</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../xc-framework-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-fastdfs</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>
2) 配置 文件
在classpath:confifig下创建fastdfs-client.properties文件
#http连接超时时间
fastdfs.connect_timeout_in_seconds=5
#tracker与storage网络通信超时时间
fastdfs.network_timeout_in_seconds = 30
#字符编码
fastdfs.charset = UTF-8
#多个traker的话后面逗号继续配置
#tracker服务器地址,多个地址中间用英文逗号分隔
fastdfs.tracker_servers = 192.168.138.129:22122
文件上传
//上传测试
@Test
public void testUpload() {
//加载配置文件,提供端口等信息
try {
ClientGlobal.initByProperties("config/fastdfs-client.properties");
//定义一个tracker客户端,用来请求trackerServer
TrackerClient trackerClient = new TrackerClient();
//连接tracker
TrackerServer trackerServer = trackerClient.getConnection();
//获取storage
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
//创建storageClient
StorageClient1 storageClient1 = new StorageClient1(trackerServer,storageServer);
//向storage服务器上传文件
//本地文件
String filePath = "F:/logo.png";
//上传成功后拿到文件id
String fileId = storageClient1.upload_file1(filePath, "png", null);
System.out.println(fileId);
// group1/M00/00/00/wKiKgV0iIOWAWnTPAAAawU0ID2Q874.png
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
}
文件下载
//下载测试
@Test
public void testDownload() {
try {
//读取配置文件
ClientGlobal.initByProperties("config/fastdfs-client.properties");
//创建tracker客户端
TrackerClient trackerClient = new TrackerClient();
//连接tracker
TrackerServer trackerServer = trackerClient.getConnection();
//tracker连接storage
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
//创建storage客户端
StorageClient1 storageClient1 = new StorageClient1(trackerServer, storageServer);
//从服务器下载文件
String fileId = "group1/M00/00/00/wKiKgV0iIOWAWnTPAAAawU0ID2Q874.png";
//获取下载路径
byte[] bytes = storageClient1.download_file1("group1/M00/00/00/wKiKgV0iIOWAWnTPAAAawU0ID2Q874.png");
//使用输出流保存文件
FileOutputStream outputStream = new FileOutputStream(new File("g:/logo.png"));
//向本地写文件
outputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
文件查询
//查询文件
@Test
public void testQueryFile() throws IOException, MyException {
ClientGlobal.initByProperties("config/fastdfs‐client.properties");
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer,
storageServer);
FileInfo fileInfo = storageClient.query_file_info("group1",
"M00/00/01/wKhlQFrKBSOAW5AWAALcAg10vf4862.png");
System.out.println(fileInfo);
}
搭建图片虚拟主机
在storage上安装Nginx
在 storage server 上安装 nginx 的目的是对外通过 http 访问 storage server 上的文 件。使用 nginx 的模块
FastDFS-nginx-module 的作用是通过 http 方式访问 storage 中 的文件,当 storage 本机没有要找的文件时向源
storage 主机代理请求文件。
- ### 在storage上安装FastDFS-nginx-module模块
将 FastDFS-nginx-module_v1.16.tar.gz 传 至 fastDFS 的 storage 服 务 器 的
/usr/local/下,执行如下命令:
cd /usr/local
tar -zxvf FastDFS-nginx-module_v1.16.tar.gz
cd FastDFS-nginx-module/src
修改 config 文件将/usr/local/路径改为/usr/
将 FastDFS-nginx-module/src 下的 mod_FastDFS.conf 拷贝至/etc/fdfs/下
cp mod_FastDFS.conf /etc/fdfs/
并修改 mod_FastDFS.conf 的内容:
vi /etc/fdfs/mod_FastDFS.conf
base_path=/home/FastDFS
tracker_server=192.168.101.3:22122
tracker_server=192.168.101.4:22122
url_have_group_name=true #url 中包含 group 名称
store_path0=/home/fastdfs/fdfs_storage #指定文件存储路径
\#如果有多个
将 libfdfsclient.so 拷贝至/usr/lib 下
cp /usr/lib64/libfdfsclient.so /usr/lib/
创建 nginx/client 目录
mkdir -p /var/temp/nginx/client
- storage上nginx 安装
将 nginx-1.8.0.tar.gz 拷贝到/usr/local 下
解压 nginx-1.8.0.tar.gz
进入 nginx-1.8.0 目录,执行如下配置命令:
下边红色部分表示添加 fastdfs-nginx-module 模块
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi \
--add-module=/usr/local/fastdfs-nginx-module/src #红色
make
make install
- nginx 配置文件
在usr/local/nginx/conf/
下新建一个 nginx 配置文件 nginx-fdfs.conf.
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
添加虚拟主机:(对上述模板进行修改)
server{
listen 80;
server_name 192.168.138.129;
location /group1/M00/{
root /home/fastdfs/fdfs_storage/data;
ngx_fastdfs_module;
}
}
说明:
server_name 指定本机 ip
location /group1/M00/:group1 为 nginx 服务 FastDFS 的分组名称,M00 是 FastDFS
自动生成编号,对应 store_path0=/home/fastdfs/fdfs_storage,如果 fastdfs定义
store_path1,这里就是 M01
- 安装完成启动storage上的nginx:
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-fdfs.conf
注意:启动如果报nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) nginx: [emerg] still could not bind() 错误那是端口占用了,使用lsof -i :80命令查看被占用端口,kill掉
测试
通过 java 客户端上传文件,使用浏览器 http 访问文件,这里访问上传图片测试的文件:
访问 storage:http://192.168.101.5/group1/M00/00/00/wKhlBVVY2M-AM_9DAAAT7-0xdqM485_big.png
ip 地址改为 192.168.101.6 也可以访问到文件,因为同一个分组的 storage 文件互相同步。
访问 tracker:
http://192.168.101.3/group1/M00/00/00/wKhlBVVY2M-AM_9DAAAT7-0xdqM485_
big.png
ip 地址改为 192.168.101.4 也可以访问到文件。
配置Nginx图片服务虚拟机主机
图片服务虚拟主机的作用是负载均衡,将图片请求转发到storage server上。
1、通过图片服务虚拟主机请求图片流程图
3、在nginx图片代理服务上配置图片服务器虚拟主机
配置 #图片服务配置了group1下的storage服务器地址 和 #学成在线图片服务
#gzip on;
#cms页面预览
upstream cms_server_pool{
server 127.0.0.1:31001 weight=10;
}
#图片服务配置了group1下的storage服务器地址
upstream img_server_pool{
#server 192.168.101.5:80 weight=10;
server 192.168.138.129:80 weight=10;
}
#upstream img_server_pool2{
#server 192.168.101.5:80 weight=10;
#server 192.168.138.129:80 weight=10;
#}
server{
listen 80;
server_name www.xuecheng.com;
ssi on;
ssi_silent_errors on;
location / {
alias F:/WebXiangmu/xuecheng/xc-ui-pc-static-portal/;
index index.html;
}
#页面预览
location /cms/preview/ {
proxy_pass http://cms_server_pool/cms/preview/;
}
}
#学成在线图片服务
server {
listen 80;
server_name img.xuecheng.com;
#个人中心
location /group1{
proxy_pass http://img_server_pool;
}
#location /group2{
#proxy_pass http://img_server_pool2;
#}
}
然后在HOSTS文件中添加 127.0.0.1 img.xuecheng.com
dns服务,再执行ipconfig/flushdns
刷新dns服务
就可以使用域名加文件id访问了
图片服务器启动 脚本
1、启动tracker:/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart
2、启动storage:/usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart
3、启动storage上的nginx:/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-fdfs.conf
4、启动图片虚拟主机 nginx