首先得知道分布式文件系统的概念,一般的文件系统,比如一台windows主机下的C盘、D盘、F盘,他们构成一个文件系统。而分布式文件系统的全部,不在一台主机上,分布在很多主机上。主机的个数可以无限增加。
fastDFS是分布式文件系统中的一种。他是一个开源的分布式文件系统,可以在github下载;大的方面来说,fastDFS由客户端和服务器两大部分组成。客户端(client)通常指我们写的程序,目前fastDFS提供了像C、JAVA、PHP等语言的API用来访问文件系统。而服务器由追踪器(tracker)和存储结点(storage)组成。以上客户端(client)、追踪器(tracker)、存储结点(storage)就是FastDFS里的三大角色。
追踪器(tracker)主要用来记录存储结点(storage)中状态信息,是客户端和存储结点通信的媒介。因为相关信息都存储在内存中,所以tracker的性能很高,一个tracker足以调度一个较大的集群。存储结点(storage)用来完成文件管理,包括文件存储、文件同步(冗余备份)、文件访问。
他们三者之间的关系:storage和client主动连接tracker,storage连接tracker单独开启一个线程,是为了向它汇报当前这个存贮节点剩余的空间多大、存储了哪些文件;client想要上传或者下载文件的时候它并不知到存储节点的IP地址,需要连接tracker,比如:如果客户端上传文件,tracker会找到一个有足够空间的存储节点,并把IP和端口返回给client,client根据这个地址结构去连接storage,然后把要上传的文件上传到这个storage中。
这个三个角色可以部署在多台主机上,也可以部署在一台主机上。
fastDFS的一些配置:
默认配置文件的位置:/etc/fdfs
1、tracker配置文件:
1 bind_addr=192.168.31.119 //当前主机的IP 2 3 port=22122 //绑定端口 4 5 base_path=/home/kitty/fastDFS/tracker //存放log日志
启动tracker,fdfs_trackerd在/usr/bin/下
1 sudo fdfs_trackerd /etc/fdfs/tracker.conf 2 3 sudo fdfs_trackerd /etc/fdfs/racker.conf restart //重启 4 5 sudo fdfs_trackerd /etc/fdfs/racker.conf stop //停止服务
2、storage的配置文件
1 group_name=group1 2 #存储节点所属的组 3 bind_addr=192.168.31.119 4 #存储节点绑定的IP 5 port=23000 6 #绑定的端口 7 base_path=/home/kitty/fastDFS/storage 8 #存储日志文件的目录 9 store_path_count=1 10 #存储目录的个数 11 store_path0=/home/kitty/fastDFS/storage 12 #配置具体的存储目录 13 tracker_server=192.168.31.119:22122 14 #连接tracker的时候使用的IP和端口
storage服务的启动
1 sudo fdfs_storaged /etc/fdfs/storage.conf 2 sudo fdfs_storaged /etc/fdfs/storage.conf restart 3 sudo fdfs_storaged /etc/fdfs/storage.conf stop
查看tracker和storage是否启动成功
3、客户端配置文件
1 base_path=/home/kitty/fastDFS/client 2 #log日志目录 3 tracker_server=192.168.31.119:22122 4 #连接tracker时候需要的iP和端口信息
fastDFS的上传流程:
fastDFS的下载流程
以上程序都完成后,可以用命令测试是否能成功上传文件:
“group1/M00/00/00/wKgfd19zXJOAGU8rAAAAXdwKmQY729.rdb”是命令执行的结果,group1是组名,M00是磁盘虚拟路径,对应storag.conf中base_path=/home/kitty/fastDFS/storage,00/00 是二级目录 , 存储上传文件的目录。 wKgfd19zXJOAGU8rAAAAXdwKmQY729.rdb是文件名。
下载
删除:
在fastDFS的软件包中有上传的示例程序和API,以下是修改后的程序:
1 //main.c 2 #include"stdio.h" 3 #include"stdlib.h" 4 #include"fdfs_api.h" 5 6 int main(int argc,char* argv[]) 7 { 8 char fileid[1024]; 9 fdfs_upload_file("/etc/fdfs/client.conf",argv[1],fileid); 10 printf("fifleID=%s\n",fileid); 11 } 12
//.h 1 #ifndef _FDFS_API_H_ 2 #define _FDFS_API_H_ 3 int fdfs_upload_file(const char*,const char*,char*); 4 #endif
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<errno.h> 5 #include<sys/types.h> 6 #include<sys/stat.h> 7 #include"fdfs_client.h" 8 #include"logger.h" 9 int fdfs_upload_file(const char*conf_file,const char*local_file,char* fileid ) 10 { 11 char group_name[FDFS_GROUP_NAME_MAX_LEN+1]; 12 ConnectionInfo *pTrackerServer;//追踪器指针 13 int result; 14 int store_path_index; //存储路径 15 ConnectionInfo storageServer; //存储器 16 17 18 19 if ((result=fdfs_client_init(conf_file)) != 0)//通过客户端的配置文件初始化一些信息 20 { 21 return result; 22 } 23 //通过配置文件中的信息,去连接追踪器,得到一个地址 24 //访问追踪器 25 pTrackerServer = tracker_get_connection(); 26 if (pTrackerServer == NULL)//失败 27 { 28 fdfs_client_destroy(); 29 return errno != 0 ? errno : ECONNREFUSED; 30 } 31 32 *group_name = '\0'; 33 //通过追踪器得到可用的存储节点信息 34 if ((result=tracker_query_storage_store(pTrackerServer,\ 35 &storageServer,group_name,&store_path_index))!=0) 36 { 37 fdfs_client_destroy(); 38 fprintf(stderr,"strcker_query_storage fail,"\ 39 "error no:%d,error info:%s\n", 40 result,STRERROR(result)); 41 return result; 42 } //开始上传,得到一个fileid 43 result=storage_upload_by_filename1(pTrackerServer,\ 44 &storageServer,store_path_index,\ 45 local_file,NULL,\ 46 NULL,0,group_name,fileid 47 ); 48 if(0==result) 49 { 50 printf("%s\n",fileid); 51 } 52 else 53 { 54 fprintf(stderr,"upload fifle fail,"\ 55 "error no :%d,error info:%s\n",\ 56 result ,STRERROR(result) 57 ); 58 } 59 tracker_disconnect_server_ex(pTrackerServer,true);//和tracker断开连接 60 fdfs_client_destroy(); 61 62 return result; 63 64 }
还可以通过管道的方法,实现文件的上传
1 //filename是要上传的文件名,fileid是传入参数,上传后得到的文件ID 2 void upload_to_storage(char *filename,char *fileid) 3 { 4 pid_t pid; 5 int fd[2]; 6 //创建管道 7 int ret = pipe(fd); 8 if(ret==-1) 9 { 10 perror("pipe error\n"); 11 exit(1); 12 } 13 //创建子进程 14 pid = fork(); 15 if(pid<0) 16 { 17 perror("fork error\n"); 18 exit(1); 19 } 20 else if(pid==0) //子进程关闭读端 21 { 22 close(fd[0]); 23 dup2(fd[1],STDOUT_FILENO); //把标准输出重定向给管道的写端 24 //开始执行上传操作 25 execlp("fdfs_upload_file","fdfs_upload_file","/etc/fdfs/client.conf",filename,NULL); 26 close(fd[1]); //关闭写端 27 } 28 else //父进程关闭写端 29 { 30 close(fd[1]); 31 read(fd[0],fileid,255); //从管道读端写数据,写到fileid中 32 wait(NULL); //回收子进程 33 close(fd[0]); 34 } 35 }
FastDFS的集群
tracker集群:Tracker server之间是相互平等关系同时提供服务,客户端请求Tracker server采用轮询方式,如果请求的 tracker无法提供服务则换另一个tracker。
storage集群:Storage集群采用了分组存储方式, 由一个或多个组构成;集群存储总容量为集群中所有组的存储容量之和;一个组由一台或多台存储服务器组成,组内的Storage server 之间是平等关系,相互备份,组内存储容量遵循木桶效应,所以同一组内的结点容量尽量差不多; 不同组的Storage server之间不会相互通信,同组内的 Storage server之间会相互连接进行文件同步,从而保证同组内每个storage上的文件完全一致的。
这里衍生出一个问题,同组内的storage是如何做到同步?
当有一个client向某一个组中storage服务器写数据,该服务器就被称为“源服务器”,这台源服务器向同组内的其他storage server以发广播形式同步。当有一台新的storage加入,会从原来的storage中选择一台作为源服务,同步给目标服务器,也就是新加入的storage