fastdfs storage server的设计与实现


 fastdfs是一个针对互联网应用设计的分布式文件系统,具有架构简单,结构清晰,代码量小等特点。 具体的介绍及架构请参考分布式文件系统FastDFS架构剖析(http://www.programmer.com.cn/4380/),这篇文章是由fastdfs的作者撰写。
因为fastdfs的轻量级特点,所以也适合广大技术爱好者学习分布式文件系统的设计及实现技术。通过深入代码,了解内部细节。本文的fastdfs版本为5.01。
服务交互场景 fastdfs采用了传统的C/S模型,服务分为client和server,之间采用私有协议通信。系统的几种交互场景: 1)client ---->> track server 2)client ---->> storage server 3)storage server ---->> track server 4)storage server ---->> storage server
其中1)和2)是client(fastdfs的用户)使用fastdfs的情况,client先根据track server得到storage server地址,然后再storage server上做操作(如上传、下载文件等)。由client发起操作。 3)是storage server向track server报告信息。由storage server主动发起。 4)storage server向同组内的其他storage server同步操作信息。同步的实现是系统中比较复杂的一块。

根据上面的交互图,storage server包括3部分功能: 1)接收并处理请求,来自client和storage,service threads 2)将自己的状态信息报告tracker server, report threads. 3)同步本地数据到同组内的其他storage server, sync threads
任务1)的启动及运行 主体实现代码,storage_service.c 根据配置文件中的定义,启动相应的service threads,线程入口storage_thread_entrance storage_deal_task:解析协议,进行相应处理。
任务2)的启动与运行 主体实现代码,tracker_client_thread.c 针对每个tracker创建一个线程(report线程),线程入口:tracker_report_thread_entrance。该任务启动过程中,storage的状态会经历一系列变化:

storage_server的状态转换图
这些状态是tracker服务器上记录的相应的storage的状态,tracker根据storage的状态来决定其是否可用。 ACTIVE是最终状态,表示storage server可以提供服务;其余是中间状态。
INIT storage server启动后,发送TRACKER_PROTO_CMD_STORAGE_JOIN到tracker,tracker将storage状态设置为FDFS_STORAGE_STATUS_INIT。在tracker_report_join中实现。
判断是否需要同步 同步:是否需要同组的其他storage server向自己同步文件。对于组中的第一个storage server,不需要从其他storage同步,因为只有一个storage;对于其他的storage server,根据g_sync_old_done判断,g_sync_old_done是全局变量,默认是false。storage第一次上线后,g_sync_old_done是false,所以会有其他storage server对自己进行同步,用来同步的源storage server有tracker进行选择,同步完成后g_sync_old_done设置为true,并被保存到文件系统中(storage_write_to_sync_ini_file)。第一次上线后的再次上线(例如系统关机维护后的重新启动),g_sync_old_done是true,就不会从源服务器同步数据。再次上线和第一次上线的区别是,再次上线的storage会有状态文件保存在本地,storage启动,会拿到这些持久化数据。
转换02,WAIT_SYNC 第一次上线的storage会经过该步骤,将状态变为FDFS_STORAGE_STATUS_WAIT_SYNC。发送TRACKER_PROTO_CMD_STORAGE_SYNC_DEST_REQ到tracker(tracker_sync_dest_req),如果tracker也决定该storage server需要从源storage进行同步,会返回源storageIP和时间戳:g_sync_src_id和g_sync_until_timestamp。tracker端会将该storage状态设置为FDFS_STORAGE_STATUS_WAIT_SYNC。
转换03,SYNCING 该转换由源storage server的同步线程进行,任务3中实现。将storage状态改为FDFS_STORAGE_STATUS_SYNCING;
转换04,OFFLINE 源storage server完成对该storage的同步后,将其状态改为OFFLINE,任务3中实现。storage的状态变为FDFS_STORAGE_STATUS_OFFLINE;

转换05,ACTIVE 该转换由tracker server完成。storage向tracker发送heart beat(tracker_heart_beat),tracker收到storage的heart beat后,如果storage的状态是OFFLINE,就将其修改成ACTIVE。pTargetServer->status = FDFS_STORAGE_STATUS_ACTIVE;
转换06,ONLINE 如果不需要从其他storage进行同步(g_sync_old_done == true),调用tracker_sync_notify,发送TRACKER_PROTO_CMD_STORAGE_SYNC_NOTIFY到tracker。tracker将storage状态变为online,FDFS_STORAGE_STATUS_ONLINE。
转换07,ACTIVE 该转换由tracker server完成。storage向tracker发送heart beat(tracker_heart_beat),tracker收到storage的heart beat后,如果storage的状态是ONLINE,就将其修改成ACTIVE。pTargetServer->status = FDFS_STORAGE_STATUS_ACTIVE;
storage状态变成ACTIVE,可以对外提供服务。状态转换涉及3个角色,一个是tracker服务器,一个是源storage服务器的同步线程,一个是storage的report线程。
进入active状态后,report线程进入主循环执行下面几个任务:   tracker_heart_beat   tracker_report_sync_timestamp   tracker_report_df_stat
任务3)的启动与运行 主体代码,storage_sync.c 针对本组中的每个storage server创建一个线程(sync线程,同步线程),线程入口:storage_sync_thread_entrance。storage server列表由tracker提供,在tracker_report_join中返回。任务3的主循环就是读取binlog,然后将binlog记录的内容,同步到组内的其他storage上去。
sync线程将自己本地保存的数据(源数据而不是副本)同步给本组的其他storage: 有两种同步模式: 1)正常同步 指同步本地的源数据。代码逻辑是读binlog记录,然后根据记录执行同步操作: storage_sync_data(&reader, &storage_server, &record))
2)追加同步 同步本地的源数据和副本数据。该模式就是上述状态转换中需要同步的情况,执行转换03和04。sync线程会根据被同步的storage和tracker分配给他的源storage,来决定是否由自己对storage进行同步。
转换03代码   if (pStorage->status == FDFS_STORAGE_STATUS_WAIT_SYNC)   {    pStorage->status = FDFS_STORAGE_STATUS_SYNCING;    storage_report_storage_status(pStorage->id, \     pStorage->ip_addr, pStorage->status);   } 判断是否需要同步副本代码: STARAGE_CHECK_IF_NEED_SYNC_OLD(pReader, pRecord) 只有针对副本(REPLICA)数据,才需要判断是否需要同步给其他storage。
转换04代码 当binlog中没有数据后(read_result == ENOENT),修改storage状态     if (pStorage->status == \      FDFS_STORAGE_STATUS_SYNCING)     {      pStorage->status = \       FDFS_STORAGE_STATUS_OFFLINE;      storage_report_storage_status( \       pStorage->id, \       pStorage->ip_addr, \       pStorage->status);     }     }
binlog文件 任务1中的service线程会把对本地的文件修改(upload, delete,修改metadata等)记录在binlog文件中。
binglog格式: 文件修改时间戳 文件操作类型   STORAGE_OP_TYPE_SOURCE_CREATE_FILE   STORAGE_OP_TYPE_SOURCE_APPEND_FILE   。。。定义在storage_sync.h 文件名字
storage_binlog_write:写记录
binlog缓存 binglog的读写通过buffer进行,buffer SYNC_BINLOG_WRITE_BUFF_SIZE。到超过buffer容量,写入磁盘。
binlog文件大小 每个binglog的大小是SYNC_BINLOG_FILE_MAX_SIZE,超过这个值,会创建新的binlog文件。
binlog文件名的最后由binlog_index确定,binlog_index从0开始递增。binlog_index是全局变量,会被持久化保存到index文件中。 #define SYNC_BINLOG_INDEX_FILENAME SYNC_BINLOG_FILE_PREFIX".index" 每次重新启动时,会根据这个index文件取到binlog_index,然后定位到需要相应的binlog文件。binlog文件会持续增加。
static char *get_writable_binlog_filename1(char *full_filename, \   const int binlog_index) {  snprintf(full_filename, MAX_PATH_SIZE, \    "%s/data/"SYNC_DIR_NAME"/"SYNC_BINLOG_FILE_PREFIX"" \    SYNC_BINLOG_FILE_EXT_FMT, \    g_fdfs_base_path, binlog_index);  return full_filename; }

如何使用binlog进行同步 sync线程会真对每个storage,将本地binlog文件中的内容全部应用到相应的storage中。 针对每个storage,synn线程会保存一份mark文件,get_mark_filename_by_reader mark文件后缀名:#define SYNC_MARK_FILE_EXT ".mark" mark文件中会保存上次同步到的状态(storage_write_to_mark_file):   MARK_ITEM_BINLOG_FILE_INDEX, pReader->binlog_index, \   MARK_ITEM_BINLOG_FILE_OFFSET, pReader->binlog_offset, \   MARK_ITEM_NEED_SYNC_OLD, pReader->need_sync_old, \   MARK_ITEM_SYNC_OLD_DONE, pReader->sync_old_done, \   MARK_ITEM_UNTIL_TIMESTAMP, (int)pReader->until_timestamp, \   MARK_ITEM_SCAN_ROW_COUNT, pReader->scan_row_count, \   MARK_ITEM_SYNC_ROW_COUNT, pReader->sync_row_count);
后面sync thread重新启动后,可以根据上次记录的位置,继续开始同步。
storage_reader_init 用来读取该mark文件(第一次上线的系统,没有该文件),加载上次同步状态。
其他状态文件 storage中还有另外2个保存持久化状态的文件 #define DATA_DIR_INITED_FILENAME ".data_init_flag" #define STORAGE_STAT_FILENAME "storage_stat.dat" 每次启动时,都会从这些文件加载信息,已恢复上次的运行状态。
STORAGE_STAT_FILENAME用来记录本storage的同步信息,如是否需要从其他源storage进行同步等。通过storage_write_to_sync_ini_file写入。
DATA_DIR_INITED_FILENAME用来保存storage上的文件状态信息,如上传文件数等。通过storage_write_to_stat_file写入。last_sync_update用来记录storage上次被同步到的时间,这个字段在tracker选择download storage服务器的时候很重要。

相关内容

    暂无相关文章