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的状态转换图

这些状态是trackerserver上记录的对应的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个角色。一个是trackerserver,一个是源storageserver的同步线程。一个是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 storageserver的时候非常重要。


随机推荐

  1. 转载 Android快捷键 转载

    一.实用类快捷键 1 常用熟悉的快捷键 CTRL+C(复制).CTRL+X(剪切).CTRL+Z(撤销).CTRL+F(查找).CTRL+H(搜索文件或字符串).CTRL+Y(重做).CTRL+/(双 ...

  2. django model 中class meta

    class Meta: ordering = ['-num', 'length'] verbose_name = 'name' verbose_name_plural = 'names' orderi ...

  3. java笔记--策略模式和简单工厂模式

    策略模式: --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3884781.html "谢谢-- 为什么使用:策略模式主要用于 ...

  4. 2016年11月6日 星期日 --出埃及记 Exodus 19:22

    2016年11月6日 星期日 --出埃及记 Exodus 19:22 Even the priests, who approach the LORD, must consecrate themselv ...

  5. leetCode 31.Next Permutation (下一个字典序排序) 解题思路和方法

    Next Permutation  Implement next permutation, which rearranges numbers into the lexicographically ne ...

  6. [SDOI2017]苹果树

    题目描述 https://www.luogu.org/problemnew/show/P3780 题解 一道思路巧妙的背包题. 对于那个奇怪的限制,我们对此稍加分析就可以发现它最后选择的区域是一个包含 ...

  7. mybatis mapper映射文件全解

    目录 select.update.delete.insert 设置参数类型以及取值 基本数据类型 对象数据类型 map数据类型 #{  } 和 ${  } 的区别 ResultMap Auto-map ...

  8. OS + CentOS / http_proxy / https_proxy / dalishangwang / repo

    s OS + Linux RedHat / redhat7 / redhat 7 / redhat 6 / redhat 5 https://lindows.iteye.com/blog/456637 ...

  9. PHP与Nginx之间的运行机制以及原理

    一.普及Nginx与Php-fpm相关知识点 Nginx是什么 Nginx ("engine x") 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服 ...

  10. MySQL命令学习

    上面两篇博客讲了MySQL的安装.登录,密码重置,为接下来的MySQL命令学习做好了准备,现在开启MySQL命令学习之旅吧. 首先打开CMD,输入命令:mysql -u root -p  登录MySQ ...