一、背景说明
FastDFS是一款开源的分布式文件系统,具体介绍就不说了,有兴趣的可以自行百度下。
以下是官方的架构图:
一次完整的写交互过程如下:
1、Client向Tracker查询可用的Storage;
2、Tracker随机返回一个Storage;
3、Client向Storage发起写请求;
一次完整的读交互:
1、Client向Tracker查询可用的Storage;
2、Tracker随机返回一个Storage;
3、Client向Storage发起读请求;
可以看到每个Storage都是对等的,即每个Storage上存储的文件都是全量的。
最近一朋友线上FastDFS服务器老是报文件不存在的错误,版本为5.11:
[2020-08-12 23:16:37] WARNING - file: storage_service.c, line: 6899, client ip: xx.xx.xxx.xxx, logic file: 06/75/xxxx.jpg not exist
架构如下:
2台Tracker,2台Storage。
每台机器上都有上述报错。
二、FastDFS同步机制分析
我们先分析FastDFS如何实现文件在不同服务器的同步的,FastDFS是以binglog的格式同步各自上传/修改的文件的,具体位置在安装目录的data/sync目录下,文件一般叫binlog.000这样,以下为我开发机的截图:
具体内容如下:
1589182799 C M00/00/00/rBVrTV65AU-ACKi2AAARqXyG2io334.jpg
第1列是时间戳,第2列是修改内容,示例中大部分是创建文件,所以是C,其它参考文件 storage/storage_sync.h:
#define STORAGE_OP_TYPE_SOURCE_CREATE_FILE 'C' //upload file
有了binglog只是保证不同服务器可以同步数据了,真正实现还有很多东西要考虑:
1、每次是全量还是增量同步,如果是增量,如何记录最后同步的位置,同步的位置做持久化吗;
2、binlog如何保证可靠性,即FastDFS实现的时候是binlog刷磁盘即fsync后才返回给客户端吗;
关于第1点,FastDFS是实现增量同步的,最后位置保存在安装目录的data/sync目录下,扩展名是mark的文件,具体格式是这样的:
172.21.107.236_23000.mark
即 IP_端口.mark。
如果集群中有两个Storage,172.21.104.36, 172.21.104.35,则在36上有1个mark文件:172.21.104.35_23000.mark,而在35上mark文件也只有1个:
172.21.104.36_23000.mark。
mark文件具体内容如下:
binlog_index=0
关键参数是binlog_offset,即binlog中最后同步成功的偏移量,每同步一个文件后,都会将偏移量更新。
那binlog是异步还是同步将binlog同步给其它的Storage呢,答案是异步,具体可以参考函数:storage_sync_thread_entrance,这个函数是线程的入口,FastDFS在启动时会启动这个线程用来同步:
int storage_sync_thread_start(const FDFSStorageBrief *pStorage)
在这个线程中,会周期地读取binlog,然后同步给其它的Storage:
while (g_continue_flag && (!g_sync_part_time || \
可以看到,这个线程周期性地调用storage_binlog_read 读取binlog,然后调用storage_sync_data同步给其它Storage,然后调用storage_write_to_mark_file 将mark文件写入到磁盘持久化。
通过上面的分析,可以判断FastDFS在异步情况下是会丢数据的,因为同步binlog给其它Storage是异步的,所以还没同步之前这台机器挂了并且起不来,数据是会丢失的;
另外binlog不是每1次都刷磁盘的,有参数设置,单位为秒:
sync_binlog_buff_interval
即保证多久将将mark文件刷新到磁盘中,果设置大于0,也是会容易丢失数据的。
三、解决方案
回到问题本身,为什么出现数据不同步呢,是因为在搭建 FastDFS的时候,运维的同学直接从其它服务器上拷过来的,包括整个data目录,也包括data下面的sync目录,这样就容易出现mark文件的偏移量不准的问题。
如何解决呢,手动修改mark文件,将binlog_offset设为0,这样FastDFS就会从头同步文件,碰到已经存在的文件,系统会略过的,这是我开发机上的日志:
[2020-08-11 20:27:36] DEBUG - file: storage_sync.c, line: 143, sync data file, logic file: M00/00/00/rBVrTV8yZl6ATOQyAAAJTMk6Vgo7337.md on dest server xx.xx.xx.xx:23000 already exists, and same as mine, ignore it
当然前提是日志级别开到DEBUG级别。
PS:
源代码中同步文件成功是没有日志的,写mark文件成功也是没有日志的,为了调试方便,我们都加上相关的调试日志了。
保存mark文件加日志可以在函数storage_write_to_mark_file中加入一条info日志。
往期精彩文章:
码字不易,如果觉得这篇文章有帮助,请关注我的个人公众号: