分布式文件系统FastDFS
FastDFS
OVERVIEW
一、fastDFS
1.fastDFS概述
fastDFS是c语言编写的一款开源的分布式文件系统(余庆淘宝架构师)。为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,注重高可用、高性能等指标。可以很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。图床、网盘
- 冗余备份: 纵向扩容
- 线性扩容: 横向扩容
fastDFS框架中的三个角色 | 具体说明 |
---|---|
追踪器Tracker - 管理者(守护进程) | 管理存储节点 |
存储节点 - storage(守护进程) | 存储节点是有多个的 |
客户端 - 开发者编写的程序(非守护进程) | 文件上传、文件下载 |
2.fastDFS模块
-
追踪器:最先启动追踪器
-
存储节点:第二个启动的角色,存储节点启动之后, 会单独开一个线程
- 汇报当前存储节点的容量, 和剩余容量
- 汇报数据的同步情况
- 汇报数据被下载的次数
-
客户端:最后启动
-
上传文件:连接追踪器, 询问存储节点的信息
-
我要上传1G的文件, 询问那个存储节点有足够的容量
-
追踪器查询, 得到结果
-
追踪器将查到的存储节点的 IP + port 发送给客户端
-
通过得到IP和端口连接存储节点
-
将文件内容发送给存储节点(Socket通信)
-
-
下载文件:连接追踪器, 询问存储节点的信息
-
询问, 要下载的文件在哪一个存储节点
-
追踪器查询, 得到结果
-
追踪器将查到的存储节点的 IP + port 发送给客户端
-
通过得到IP和端口连接存储节点
-
下载文件(Socket通信)
-
-
3.fastDFS集群
(1)追踪器集群:
为了避免单点故障,可以建立追踪器集群。多个Tracker追踪器通过轮训的方式来进行工作,可以通过修改配置文件的方式实现集群。
(2)存储节点集群:
- fastDFS管理存储节点的方式:通过分组的方式完成的
- 集群方式(扩容方式)
- 横向扩容 - 增加容量
- 添加一台新的主机 -> 容量增加了
- 假设当前有两个组: group1, group2,需要添加一个新的分组 -> group3,新主机属于第三组
- 不同组的主机之间不需要通信
- 纵向扩容 - 数据备份
- 假设当前有两个组: group1, group2
- 将新的主机放到现有的组中
- 每个组的主机数量从1 -> n
- 这n台主机的关系就是相互备份的关系
- 同一个组中的主机需要通信
- 每组的容量 == 容量最小的这台主机
- 假设当前有两个组: group1, group2
- 横向扩容 - 增加容量
二、fastDFS使用
1.fastDFS的安装
- libfastcommon-1.36.zip(fastdfs的基础库)
- unzip xxx.zip
- ./make.sh
- ./make.sh install
- fastdfs-5.10.tar.gz
- tar zxvf xxx.tar.gz
- ./make.sh
- ./make.sh install
- 测试
#fastDFS安装的所有的可执行程序:
/usr/bin/fdfs_*
fdfs_test
2.fastDFS配置文件
配置文件默认位置: /etc/fdfs,
文件夹内容:client.conf.sample storage.conf.sample storage_ids.conf.sample tracker.conf.sample
-
tracker 配置文件
# 将追踪器和部署的主机的IP地址进程绑定, 也可以不指定 # 如果不指定, 会自动绑定当前主机IP, 如果是云服务器建议不要写 bind_addr=192.168.247.135 # 追踪器监听的端口 port=22122 # 追踪器存储日志信息的目录, xxx.pid文件, 必须是一个存在的目录 base_path=/home/yuqing/fastdfs
-
storage 配置文件
# 当前存储节点对应的主机属于哪一个组 group_name=group1 # 当前存储节点和所应该的主机进行IP地址的绑定, 如果不写, 有fastdfs自动绑定 bind_addr= # 存储节点绑定的端口 port=23000 # 存储节点写log日志的路径 base_path=/home/yuqing/fastdfs # 存储节点提供的存储文件的路径个数 store_path_count=2 # 具体的存储路径 store_path0=/home/yuqing/fastdfs store_path1=/home/yuqing/fastdfs1 # 追踪器的地址信息 tracker_server=192.168.247.135:22122 tracker_server=192.168.247.136:22122
-
客户端配置文件
# 客户端写log日志的目录 # 该路径必须存在 # 当前的用户对于该路径中的文件有读写权限 # 当前用户robin # 指定的路径属于root base_path=/home/yuqing/fastdfs # 要连接的追踪器的地址信息 tracker_server=192.168.247.135:22122 tracker_server=192.168.247.136:22122
3.fastDFS的启动
-
第一个启动追踪器 - 守护进程
# 启动程序在 /usr/bin/fdfs_* # 启动 fdfs_trackerd 追踪器的配置文件(/etc/fdfs/tracker.conf) # 关闭 fdfs_trackerd 追踪器的配置文件(/etc/fdfs/tracker.conf) stop # 重启 fdfs_trackerd 追踪器的配置文件(/etc/fdfs/tracker.conf) restart
-
第二个启动存储节点 - 守护进程
# 启动 fdfs_storaged 存储节点的配置文件(/etc/fdfs/stroga.conf) # 关闭 fdfs_storaged 存储节点的配置文件(/etc/fdfs/stroga.conf) stop # 重启 fdfs_storaged 存储节点的配置文件(/etc/fdfs/stroga.conf) restart
-
最后启动客户端 - 普通进程
# 上传 fdfs_upload_file 客户端的配置文件(/etc/fdfs/client.conf) 要上传的文件 # 得到的结果字符串: group1/M00/00/00/wKj3h1vC-PuAJ09iAAAHT1YnUNE31352.c # 下载 fdfs_download_file 客户端的配置文件(/etc/fdfs/client.conf) 上传成功之后得到的字符串(fileID)
-
fastDFS状态检测
fdfs_monitor /etc/fdfs/client.conf
# FDFS_STORAGE_STATUS:INIT :初始化,尚未得到同步已有数据的源服务器 # FDFS_STORAGE_STATUS:WAIT_SYNC :等待同步,已得到同步已有数据的源服务器 # FDFS_STORAGE_STATUS:SYNCING :同步中 # FDFS_STORAGE_STATUS:DELETED :已删除,该服务器从本组中摘除 # FDFS_STORAGE_STATUS:OFFLINE :离线 # FDFS_STORAGE_STATUS:ONLINE :在线,尚不能提供服务 # FDFS_STORAGE_STATUS:ACTIVE :在线,可以提供服务
Storage Server的7种状态:https://blog.csdn.net/u014723529/article/details/46048411
4.对file_id的解释
-
group1
- 文件上传到了存储节点的哪一个组
- 如果有多个组这个组名可变的
-
M00 - 虚拟目录
-
和存储节点的配置项有映射
store_path0=/home/yuqing/fastdfs/data -> M00
store_path1=/home/yuqing/fastdfs1/data -> M01
-
-
00/00
- 实际的路径
- 可变的
-
wKhS_VlrEfOAdIZyAAAJTOwCGr43848.md
- 文件名包含的信息
- 采用Base64编码:包含的字段包括
-
源storage server Ip 地址
-
文件创建时间
-
文件大小
-
文件CRC32效验码 :循环冗余校验
-
随机数
-
三、上传下载代码实现
1.使用多进程方式实现
获取最终上传的文件id:file_id的程序,
-
exec函数族函数
- execl
- execlp
-
子进程 -> 执行execlp(“fdfs_upload_file” , “xx”, arg, NULL), 有结果输出, 输出到终端
- 不让它写到终端 -> 重定向dup2(old, new),old-> 标准输出、new -> 管道的写端
- 文件描述符
- 数据块读到内存 -> 子进程
- 数据最终要给到父进程
- 进程间通信:pipe -> 读端, 写端
- 在子进程创建之前创建就行了
-
父进程
- 读管道 -> 内存
- 内存数据写入数据库
2.使用fastDFS 提供的API实现
将fastDFS 提供的API,进行针对业务的修改,也能实现文件的上传操作:
/
* Copyright (C) 2008 Happy Fish / YuQing
*
* FastDFS may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fdfs_client.h"
#include "logger.h"static void usage(char *argv[]) {printf("Usage: %s <config_file> <local_filename> " \\"[storage_ip:port] [store_path_index]\\n", argv[0]);
}int main(int argc, char *argv[]) {char *conf_filename;char *local_filename;char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];ConnectionInfo *pTrackerServer;int result;int store_path_index;ConnectionInfo storageServer;char file_id[128];if (argc < 3) {usage(argv);return 1;}log_init();g_log_context.log_level = LOG_ERR;ignore_signal_pipe();conf_filename = argv[1];if ((result=fdfs_client_init(conf_filename)) != 0) {return result;}pTrackerServer = tracker_get_connection();if (pTrackerServer == NULL) {fdfs_client_destroy();return errno != 0 ? errno : ECONNREFUSED;}local_filename = argv[2];*group_name = '\\0';if (argc >= 4) {const char *pPort;const char *pIpAndPort;pIpAndPort = argv[3];pPort = strchr(pIpAndPort, ':');if (pPort == NULL) {fdfs_client_destroy();fprintf(stderr, "invalid storage ip address and " \\"port: %s\\n", pIpAndPort);usage(argv);return 1;}storageServer.sock = -1;snprintf(storageServer.ip_addr, sizeof(storageServer.ip_addr), \\"%.*s", (int)(pPort - pIpAndPort), pIpAndPort);storageServer.port = atoi(pPort + 1);if (argc >= 5) {store_path_index = atoi(argv[4]);} else {store_path_index = -1;}} else if ((result=tracker_query_storage_store(pTrackerServer, \\&storageServer, group_name, &store_path_index)) != 0) {fdfs_client_destroy();fprintf(stderr, "tracker_query_storage fail, " \\"error no: %d, error info: %s\\n", \\result, STRERROR(result));return result;}result = storage_upload_by_filename1(pTrackerServer, \\&storageServer, store_path_index, \\local_filename, NULL, \\NULL, 0, group_name, file_id);if (result == 0) {printf("%s\\n", file_id);} else {fprintf(stderr, "upload file fail, " \\"error no: %d, error info: %s\\n", \\result, STRERROR(result));}tracker_disconnect_server_ex(pTrackerServer, true);fdfs_client_destroy();return result;
}
补充:Linux下进行源码安装
- 找可执行文件 configure
- 执行这个可执行文件
- 检测安装环境
- 生成 makefile
- 执行这个可执行文件
- 执行make命令
- 编译源代码
- 生成动态库
- 生成静态库
- 可执行程序
- 编译源代码
- 安装 make install (需要管理员权限)
- 将第三步生成的动态库/动态库/可执行程序拷贝到对应的系统目录