安装同Tracker。
配置Storage(vim /etc/fdfs/storage.conf):
启动Storage:
自启动Storage:
FastDFS基本操作
集群监控
Tracker和Storage都启动好后,可以通过fdfs_monitor监控当前集群情况:
文件上传
文件上传可通过fdfs_test进行测试:
文件下载
文件下载也可通过fdfs_test进行测试:
文件访问
通常,对于图片和文件的访问,是不太可能走TCP,而是通过简单的HTTP访问,这时需要通过一些Web服务器(如nginx,apache)来代理,fastdfs也有了nginx的支持,下面则将通过安装nginx来完成文件访问,之前的开发环境将变为:
在Storage上配置fastdfs-nginx-module所需的配置文件:
在Storage上安装nginx:
在其他服务器上安装用于图片文件负载的nginx:
下面是一种可供参考的图片文件服务器部署策略:
FastDFS的一些运行机制
Client与Tracker通讯
Client大部分的操作过程都是要先查询Tracker,Tracker返回目标Storage IP,然后Client再连接到该Storage,执行具体的操作。
上传文件时,如何选择Storage Group,Storage及Storage Path:
除上述策略外,Storage需满足两个条件,才能最终被选择:Storage的状态为ACTIVE,及Storage的空闲空间大于配置的预留空间。
下载文件时,如何选择Storage:
Client下载时,需要传入对应的group,因此只需再选择其中一个Storage:
删除文件时,策略同下载文件。
同组Storage之间的文件同步
FastDFS通过在同组内的多个Storage同步文件来保证系统的容错性(Fault Tolerance)。当Storage启动时,会为同组内的其他Storage分别开启一个线程,进行文件同步。文件同步主要通过binlog来实现,该文件被放置在
/path/to/storage_data_path/sync下:
binlog.index中记录了当前binlog文件的索引号,如0表示binlog.000,1表示binlog.001。
binlog.000中则记录了各项操作,如第一行:
1462789147
|
操作的时间戳。
|
C
|
操作类型。其中大写字母的操作表示为源操作,小写字母的操作表示为备份操作(由同组其他Storage同步的操作),如:
C表示源创建、c表示备份创建;
A表示源追加、a表示备份追加;
D表示源删除、d表示备份删除;
U表示源更新(如元数据)、u表示备份更新;
M表示源修改(部分修改)、m表示备份修改;
T表示源截取、t表示备份截取;
L表示源创建符号链接、l表示备份创建符号链接;
|
M00/00/00/CnBYalc5npuANW f2AAAALHr0pq04296.sh
|
文件ID。文件ID由以下几部分组成:
M00:Storage配置的虚拟路径,与选项store_path*对应;
/00/00:Storage在每个虚拟磁盘路径下创建的两级目录;
CnBYalc5npuANWf2AAAALHr0pq04296.sh:由Storage生成,其中包括源Storage IP,文件创建时间戳,文件大小,crc32码及文件扩展名。
|
还有比较重要mark文件,该文件描述了当前Storage同步源操作到同组内其他Storage的进度:
从该文件中可以知道,当前同步给10.112.88.109这台Storage的binlog文件为binlog.000,已同步到偏移量为9722的位置,恰好为binlog.000的文件大小,说明已经完全同步。need_sync_old表示是否需要同步旧文件给对方,sync_old_done表示若需要同步旧文件,那么旧文件是否同步完成,until_timestamp表示源主机同步的操作截止时间,scan_row_count表示扫描过的操作记录行数,sync_row_count表示同步完的操作记录行数,
文件同步延迟问题
当用户上传文件后,还未完全同步到同组内其他Storage时,那么用户下载文件时,如何避免文件同步延迟问题呢?
一个最简单的解决办法则是,优先选择源Storage下载文件即可,这可以在Tracker的配置文件中设置,对应的参数名为download_server。另外一种选择Storage的方法是轮询选择(round-robin),当Client询问Tracker时,有哪些Storage可以下载指定文件呢?Tracker将返回满足如下四个条件之一的Storage:
-
1. 该Storage是该文件的源Storage,文件ID中可以解析出其源Storage IP;
-
2. 文件的创建时间戳 < Storage被同步到的文件时间戳,这意味着该文件已经同步了该Storage上。
-
3. 文件的创建时间戳 = Storage被同步到的文件时间戳,且(当前时间—文件创建时间戳) > 一个文件同步完成需要的最大时间(可配置storage_sync_file_max_time,如5分钟);
-
4. (当前时间 — 文件创建时间戳) > 文件同步延迟阈值(可配置storage_sync_file_max_delay,比如把阈值设置为1天,表示文件同步在1天内肯定可以完成)。
那么Tracker如何知道各组内Storage之间的同步进度呢,Storage会为每个Tracker分别开启一个线程,将同步信息报道给Tracker,其中当前Storage被同步的最近时间戳为同组内其他Storage同步的最小时间戳,如一组内有Storage-A、Storage-B、Storage-C三台机器,B最后同步给A的Binlog-timestamp为100,C最后同步给A的Binlog-timestamp为200,那么A机器的被同步最小时间戳就为100。Tracker会将该信息记录到storage_sync_timestamp.dat文件中:
如第一行,在组group1内,10.112.88.106这台Storage同步给10.112.88.109的最后时间戳为1463739193,同步给10.112.88.151的最后时间戳为1463739193,而第一个0表示自己同步自己。那么从第三列可以看出,10.112.88.106被同步的最小时间戳为1463739079。上述文件storage_sync_timestamp.dat,并不会被实时更新(当Tracker重启时会更新),Storage同步信息会实时在Tracker内存中,可通过fdfs_monitor查看。
集群扩展
增加Storage Group
增加Storage Group将直接对集群进行容量扩展。这也是后期维护比较常见的操作,新组添加进来后,若配置了store_lookup=2时,新加入的组将在一段时间内成为文件上传的单点,有可能对性能有所下降,比较建议在新加组时,能添加1个以上的组。
增加Storage
在同组内添加Storage后,将会开启一个叫源同步的过程,也就是从组内现有的一台机器(这台机器称为源机器)上同步历史数据到新机器的过程。
源机器选择:
当Storage是首次加入组时,会向Tracker集群中的一个发送TRACKER_PROTO_CMD_STORAGE_SYNC_DEST_REQ(87)命令,向Tracker查询,由同组内哪一台Storage作为源机器。当Tracker收到该请求后,根据当前组内的机器状态,若组内没有机器,则告诉新机器不需要进行源同步;若组内有机器但是没有状态为ACTIVE的机器,那么返回一个错误,新机器将睡眠后重试;若组内有机器并且有状态为ACTIVE的机器,那么选择一台作为其源,返回两个值:当前时间作为源同步截止时间与源机器IP,新机器将该信息记录在本地(/mnt/fastdfs/data/.data_init_flag),同时将自己状态设置成WAIT_SYNC:
源同步过程:
对于源机器,如10.112.88.106,通过与Tracker通讯,得知有新机器加入,如10.112.88.109,于是启动一个线程与10.112.88.109进行通讯;再通过Tracker查询10.112.88.109的源机器IP和源同步截止时间,发现自己是其源机器,并在本地创建10.112.88.109_23000.mark,并写入need_sync_old=1; sync_old_done=0; util_timestamp = 查询到的源同步截止时间;接着请求Tracker将10.112.88.109的状态设置成SYNING,然后开始读取binlog中的源操作同步给10.112.88.109,直到在某一个时刻,取不到更多的binlog时,请求Tracker将10.112.88.109状态设置成OFFLINE,此时源同步完成;在下一个心跳中,Tracker会将10.112.88.109状态设置成ACTIVE。
对于非源机器,如10.112.88.151,也会在本地创建10.112.88.109_23000.mark,并写入need_sync_old=0; sync_old_done=0; util_timestamp = 0;此时,并不会对10.112.88.109进行同步操作,而会等等待10.112.88.109状态为ACTIVE后,才开始同步操作。
Tracker-Leader Server
在FastDFS之中,可以配置多个Tracker,在运行过程中会选择其中一个作为Leader,由该Leader执行一些选举操作。在早期版本中Tracker-Leader有两个作用,分别是:为新加入的Storage分配一个源Storage;为开启合并存储的Group选择Trunk-Server。但是在最新的版本中实际上只有第二个作用,也就是选择Trunk-Server。对于新加入Storage分配源Storage其实是任何一个Tracker都可以进行的,为了避免多次分配,在Storage请求Tracker分配源时进行了互斥量同步。
Tracker-Leader选择过程
-
1. 向所有的Tracker(包括自己)发送一个TRACKER_GET_STATUS命令,来获取对方的状态信息,该信息包括:是否为Leader,到目前的运行时间和上次停止时间间隔(也就是最后一次停止到启动的时间间隔),只要获取到至少一个该状态成功,则继续下一步;
-
2. 对所有返回成功的Tracker-Status进行排序,获得状态值最高的Tracker;
-
3. 若状态最高的Tracker是自己,那么进入一个两阶段提交协议:
通知除自己之外的所有Tracker,将要变更Tracker-Leader,只要有一个Tracker通知成功,则进入下一步;
通知所有的Tracker(包括自己),将Leader变更成自己,只要有一个Tracker返回成功,则表示整个变更成功(此处通知了自己,那么这个步骤肯定会成功)。
-
4. 若最高状态的Tracker当前就是Leader,那么就设置本地标志为g_tracker_servers.leader_index为该Tracker。
-
否则,等待真正的Tracker Leader发起Leader变更消息;
文件合并存储
大多数文件系统,对于海量小文件的存储时,都会面临LOSF问题,比较通用的解决方案则是文件合并,FastDFS也提供了该功能,需要对Tracker作相关配置:
当启用文件合并后,上传文件返回的文件ID将不能与Storage中存储路径上的文件一一对应了,而是多个文件会合并存在在一个trunk文件中,不难想到,返回的文件ID中包含trunk文件,源文件所在trunk文件的offset等信息:
如何管理合并存储中的空闲内存
一个Trunk文件默认大小为64MB,因此创建Trunk文件时,很可能会产生空余空间,比如为存储一个10MB文件,创建一个Trunk文件,那么就会剩下接近54MB的空间(忽略其真实文件的一些头信息),下次若再次存储10MB文件时,就不需要创建新的Trunk文件文件,而是存储在现有的Trunk文件。此外当删除一个文件时,也会产生空余空间。
在Storage内部会为每个store_path维护一颗以空闲块大小作为关键字的平衡树,相同大小的空闲块保存在链表之中。每当需要存储一个文件时会首先到平衡树中查找一块最相近的空闲内存快,若该内存快有余,则被加入到平衡树中;若此时找不到一块能容纳新文件的空闲内存,则会创建一个新的trunk文件。
由Trunk Server分配空闲内存
若同组内Storage均能为新上传的文件分配空闲内存,则有可能由于该组Storage文件同步延迟问题,导致数据存储冲突,如文件a上传时,由Storage A将其分配到000001这个trunk文件的起始位置,若还未来得及同步到同组的Storage B,此时文件b上传,由Storage B也将其分配到000001这个trunk文件的起始位置,这时就发生了存储冲突,这其实只要能保证Storage A和Storage B共享同一份空闲内存平衡树数据是可以解决的,但一旦涉及到网络,则有可能发生延迟,因此需要一个专门用于分配空闲内存的角色,即Trunk Server。下图则是空闲内存分配过程:
Trunk文件同步
相对于非trunk文件,trunk文件的同步也是通过binlog文件(TrunkBinlog文件)的方式进行,格式如下:
Trunk Server选择
之前提到过,Trunk Server是由Tracker Leader进行选举,大致过程如下:
-
1. 依次向组内当前状态为ACTIVE的Storage发送TRUNK_GET_BINLOG_SIZE命令,来查询每个Storage当前保存的Trunk-Binlog的文件大小,来找到Trunk-Binlog文件最大的Storage;
-
2. 若该Group的最后一个Trunk Server与要设置的新的Trunk Server并非同一个,则像该新的Trunk Server发送TRUNK_DELETE_BINLOG_MARKS命令,让其删除从trunk-binlog同步给组内其他Storage的mark文件。(既然这个Trunk Server是新的,那么就要清除trunk-binlog的同步状态,使其从头同步trunk-binlog给组内的其他Storage);
-
3. 变更该组的Trunk Server,并将修改写入到storage_groups_new.dat文件之中,更新该组的最后Trunk Server设置,设置Trunk Server已经变更标志,该标志使得在与Storage的心跳中通知对方Trunk Server已经变更。
总结
以上,则是关于FastDFS的一些实践,对于一些图片,文件的存储,也算是一种比较轻量的解决方案。
参考文献
http://tech.uc.cn/?p=221
http://blog.csdn.net/hfty290/article/details/42064429
http://blog.csdn.net/hfty290/article/details/42041155
http://blog.csdn.net/hfty290/article/details/42041953
http://blog.csdn.net/hfty290/article/details/42030339
http://blog.csdn.net/liuaigui/article/details/9981135
http://blog.csdn.net/hfty290/article/details/42026215