FastDFS文件同步机制

  • 一、tracker server目录及文件结构
  • 二、storage server目录及文件结构
  • 三、FastDFS文件同步
    • 3.1、同步日志所在目录
    • 3.2、binlog格式
    • 3.3、同步规则
    • 3.4、Binlog同步过程
      • 3.4.1、tracker_report_thread_entrance
      • 3.4.2、storage_sync_thread_entrance
      • 3.4.3、同步前删除
    • 3.5、Storage的最后最早被同步时间
    • 3.6 新增节点的同步流程
    • 3.7、Tracker选择客户端下载文件的storage的原则
  • 总结

一、tracker server目录及文件结构

数据文件storage_groups.dat和storage_servers.dat中的记录之间以换行符(\n)分隔,字段之间以英文逗号(,)分隔。

# 我的tracker server目录是/home/fastdfs/tracker_22122,看个人配置
/home/fastdfs/tracker_22122
# 文件结构
├── data
│   ├── fdfs_trackerd.pid
│   ├── storage_changelog.dat   #storage有修改过ip
│   ├── storage_groups_new.dat  # 存储分组信息
│   ├── storage_servers_new.dat # 存储服务器列表
│   └── storage_sync_timestamp.dat # 同步时间戳
└── logs └── trackerd.log # Server日志文件

可以使用tail -f logs/trackerd.log查看打印信息,这对有报错时进行日志分析非常有用。
(1)storage_changelog.dat。例如:

1645866390 group1 192.168.0.104 192.168.0.105

(2)storage_groups_new.dat。例如:

# global section
[Global] group_count=1
# group: group1
[Group001] group_name=group1 storage_port=23000 storage_http_port=8888 store_path_count=1 subdir_count_per_path=256 current_trunk_file_id=0 trunk_server= last_trunk_server=

(3)storage_servers.dat。例如:

[Storage001]
group_name=group1 ip_addr=192.168.0.104status=7 version=6.07 join_time=1646292828 ....
[Storage002] group_name=group1 ip_addr=192.168.0.105status=7 version=6.07 join_time=1646292925 storage_port=23000....

(4)主要参数如下:
group_name:所属组名。
ip_addr:ip地址。
status:状态。
sync_src_ip_addr:向该storage server同步已有数据文件的源服务器。
sync_until_timestamp:同步已有数据文件的截至时间(UNIX时间戳)。
stat.total_upload_count:上传文件次数。
stat.success_upload_count:成功上传文件次数。
stat.total_set_meta_count:更改meta data次数。
stat.success_set_meta_count:成功更改meta data次数。
stat.total_delete_count:删除文件次数。
stat.success_delete_count:成功删除文件次数。
stat.total_download_count:下载文件次数。
stat.success_download_count:成功下载文件次数。
stat.total_get_meta_count:获取meta data次数。
stat.success_get_meta_count:成功获取meta data次数。
stat.last_source_update:最近一次源头更新时间(更新操作来自客户端)。
stat.last_sync_update:最近一次同步更新时间(更新操作来自其他storage server的同步)。

二、storage server目录及文件结构

|__data
|   |__.data_init_flag # 当前storage server初始化信息
|   |__storage_stat.dat #当前storage server统计信息
|   |__sync #存放数据同步相关文件
|   |   |__binlog.index #当前的binlog(更新操作日志)文件索引号
|   |   |__binlog.xxx #存放更新操作记录(日志)
|   |   |__${ip_addr}_${port}.mark #存放向目标服务器同步的完成情况
|   |__一级目录 #256个存放数据文件的目录,目录名为十六进制字符,如:00, 1F
|   |   |__二级目录 #256个存放数据文件的目录,目录名为十六进制字符,如:0A, CF |__logs|__storaged.log  #storage server日志文件

重点了解sync 目录及文件结构。
(1)binlog.index中只有一个数据项:当前binlog的文件索引号 binlog.###。
(2)binlog.###为索引号对应的3位十进制字符,不足三位,前面补0。索引号基于0,最大为999。一个binlog文件最大为1GB。记录之间以换行符(\n)分隔,字段之间以英文空格分隔。字段依次为:

  1. timestamp:更新发生时间(Unix时间戳)。
  2. op_type:操作类型,一个字符。
  3. filename:操作(更新)的文件名,包括相对路径,如:5A/3D/FE_93_SJZ7pAAAO_BXYD.S

(3)$ {ip_addr}_${port}.mark:ip_addr为同步的目标服务器IP地址,port为本组storage server端口。例如:10.0.0.1_23000.mark。各个参数如下:

  1. binlog_index:已处理(同步)到的binlog索引号。
  2. binlog_offset:已处理(同步)到的binlog文件偏移量(字节数)。
  3. need_sync_old:同步已有数据文件标记,0表示没有数据文件需要同步。
  4. sync_old_done:同步已有数据文件是否完成标记,0表示未完成,1表示已完成 (推送方标记)。
  5. until_timestamp:同步已有数据截至时间点(UNIX时间戳) (推送方) 上次同步时间结点。
  6. scan_row_count:总记录数。
  7. sync_row_count:已同步记录数。

(4)如果还有其他storage,则有更多的KaTeX parse error: Expected group after '_' at position 10: {ip_addr}_̲{port}.mark,比如10.0.0.2_23000.mark 。

三、FastDFS文件同步

思考:文件上传成功后,其它的storage server才开始同步,其它的storage server怎么去感知?tracker server是怎么通知storage server?

storage定时发送心跳包到tracker,并附带同步的时间节点,tracker会返回其他storage的状态。

正常文件上传完成后,就记录进binlog缓存中,系统定时刷入binlog文件。
系统有线程定时读取binlog文件,当有新增行时,判断该记录是源文件记录还是副本文件记录。
系统只主动发送源文件,副本文件不做处理(非启动时流程)。

线程:

  1. tracker_report_thread_entrance ,连接tracker有独立的线程,连接n个tracker就有n个线程。
  2. storage_sync_thread_entrance ,给同group的storage做同步,同组有n个storage,就有n-1个线程。

storage的状态:

#define FDFS_STORAGE_STATUS_INIT         0   // 初始化,尚未得到同步已有数据的源服务器
#define FDFS_STORAGE_STATUS_WAIT_SYNC   1   // 等待同步,已得到同步已有数据的源服务器
#define FDFS_STORAGE_STATUS_SYNCING     2   // 同步中
#define FDFS_STORAGE_STATUS_IP_CHANGED  3
#define FDFS_STORAGE_STATUS_DELETED     4   // 已删除,该服务器从本组中摘除
#define FDFS_STORAGE_STATUS_OFFLINE     5   // 离线
#define FDFS_STORAGE_STATUS_ONLINE      6   // 在线,尚不能提供服务
#define FDFS_STORAGE_STATUS_ACTIVE      7   // 在线,可以提供服务
#define FDFS_STORAGE_STATUS_RECOVERY    9

同步命令:

#define STORAGE_PROTO_CMD_SYNC_CREATE_FILE   16 //新增文件
#define STORAGE_PROTO_CMD_SYNC_DELETE_FILE  17 // 删除文件
#define STORAGE_PROTO_CMD_SYNC_UPDATE_FILE  18 // 更新文件
#define STORAGE_PROTO_CMD_SYNC_CREATE_LINK  19 // 创建链接

3.1、同步日志所在目录

例如192.168.0.104服务器的sync log:

192.168.0.105_23000.mark # 同步状态文件,对应发送同步的storage,记录本机到192.168.0.105的同步状态, 文件名由同步源IP_端口组成。
binlog.000 # 本地的binglog日志,文件大小最大1G,超过1G,会重新写下个文件,可以binlog.001,binlog.002,...,同时更新 binlog.index 文件中索引值
binlog_index.dat # 记录了当前写binlog的索引id。

如果有不只2个storage的时候,则该目录还有更多的 .mark文件。

3.2、binlog格式

FastDFS文件同步采用binlog异步复制方式。storage server使用binlog文件记录文件上传、删除等操作,根据binlog进行文件同步。binlog中只记录文件ID和操作,不记录文件内容。
例如:

1646123002 C M00/00/00/oYYBAF285cOIHiVCAACI-7zX1qUAAAAVgAACC8AAIkT490.txt
1646123047 c M00/00/00/oYYBAF285luIK8jCAAAJeheau6AAAAAVgABI-cAAAmS021.xml
1646123193 A M00/00/00/rBMYd2IaLXqASSVXAAAHuj79dAY65.txt 6 6
1646123561 d M00/00/00/oYYBAF285luIK8jCAAAJeheau6AAAAAVgABI-cAAAmS021.xml

可以看到,binlog文件有三列,依次为:时间戳,操作类型,文件ID(不带group名称)。

文件操作类型采用单个字母编码,其中源头操作用大写字母表示,被同步的操作为对应的小写字母。文件操作字母含义如下:

副本
C :上传文件(upload) c:副本创建
D:删除文件(delete) d:副本删除
A:追加文件(append) a:副本追加
M:部分文件更新(modify) m:副本部分文件更新(modify)
U:整个文件更新(set metadata) u:副本整个文件更新(set metadata)
T:截断文件(truncate) t:副本截断文件(truncate)
L:创建符号链接(文件去重功能,相同内容只保存一份) l:副本创建符号链接(文件去重功能,相同内容只保存一份)

注意:源表示客户端直接操作的那个Storage即为源,,其他的Storage都为副本。

同组内的storage server之间是对等的,文件上传、删除等操作可以在任意一台storage server上进行。
文件同步只在同组内的storage server之间进行,采用push方式,即源头服务器同步给本组的其他存储服务器。对于同组的其他storage server,一台storage server分别启动一个线程进行文件同步。

文件同步采用增量方式,记录已同步的位置到mark文件中。mark文件存放路径为:

$base_path/data/sync/

mark文件内容示例:

binlog_index=0      //binlog索引id 表示上次同步给114.215.169.67机器的最后一条binlog文件索引
binlog_offset=3944 //当前时间binlog 大小 (单位是字节)表示上次同步给其他机器的最后一条binlog偏移量,若程序重启了,也只要从这个位置开始向后同步即可。
need_sync_old=1    //是否需要同步老数据
sync_old_done=1    //是否同步完成
until_timestamp=1621667115 //同步已有数据文件的截至时间
scan_row_count=68 //扫描记录数
sync_row_count=53 //同步记录数

3.3、同步规则

(1)只在同组内的storage server之间进行同步。
(2) 源头数据才需要同步,备份数据不需要再次同步,否则就构成环路了,源数据和备份数据区 分是用binlog的操作类型来区分,操作类型是大写字母,表示源数据,小写字母表示备份数据。
(3)当新增一台storage server时,由已有的一台storage server将已有的所有数据(包括源头数据和备份数据)同步给该新增服务器。

增量同步:storage server之间已经在运行,它们之间的数据同步就是增量同步发。
全量同步:新增一台storage server时,由已有的一台storage server将已有的所有数据(包括源头数据和备份数据)同步给该新增服务器,这就是全量同步。

3.4、Binlog同步过程

在FastDFS之中,每个Storaged之间的同步都是由一个独立线程负责的,该线程中的所有操作都是以同步方式执行的。比如一组服务器有A、B、C三台机器,那么在每台机器上都有两个线程负责同步,如A机器,线程1负责同步数据到B,线程2负责同步数据到C。

3.4.1、tracker_report_thread_entrance

tracker_report_thread_entrance 线程负责向tracker上报信息。 在Storage.conf配置文件中,只配置了Tracker的IP地址,并没有配置组内其他的Storage。因此同组的其他Storage必须从Tracker获取。具体过程如下:

  1. Storage启动时为每一个配置的Tracker启动一个线程负责与该Tracker的通讯。
  2. 默认每间隔30秒,与Tracker发送一次心跳包,在心跳包的回复中,将会有该组内的其他Storage信息
  3. Storage获取到同组的其他Storage信息之后,为组内的每个其他Storage开启一个线程负责同步。

3.4.2、storage_sync_thread_entrance

storage_sync_thread_entrance 同步线程。每个同步线程负责到一台Storage的同步,以阻塞方式进行。

(1)打开对应Storage的mark文件,如负责到192.168.0.104的同步则打开192.168.0.104_23000.mark文件,从中读取binlog_index、binlog_offset两个字段值,如取到值为:0、100,那么就打开binlog.000文件,seek到100这个位置。
(2)进入一个while循环,尝试着读取一行,若读取不到则睡眠等待。若读取到一行,并且该行的操作方式为源操作,如C、A、D、T(大写的都是),则将该行指定的操作同步给对方(非源操作不需要同步),同步成功后更新binlog_offset标志,该值会定期写入到192.168.0.104_23000.mark文件之中。

#mermaid-svg-05C2FWgRCuH0hVSe {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-05C2FWgRCuH0hVSe .error-icon{fill:#552222;}#mermaid-svg-05C2FWgRCuH0hVSe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-05C2FWgRCuH0hVSe .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-05C2FWgRCuH0hVSe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-05C2FWgRCuH0hVSe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-05C2FWgRCuH0hVSe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-05C2FWgRCuH0hVSe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-05C2FWgRCuH0hVSe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-05C2FWgRCuH0hVSe .marker.cross{stroke:#333333;}#mermaid-svg-05C2FWgRCuH0hVSe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-05C2FWgRCuH0hVSe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-05C2FWgRCuH0hVSe .cluster-label text{fill:#333;}#mermaid-svg-05C2FWgRCuH0hVSe .cluster-label span{color:#333;}#mermaid-svg-05C2FWgRCuH0hVSe .label text,#mermaid-svg-05C2FWgRCuH0hVSe span{fill:#333;color:#333;}#mermaid-svg-05C2FWgRCuH0hVSe .node rect,#mermaid-svg-05C2FWgRCuH0hVSe .node circle,#mermaid-svg-05C2FWgRCuH0hVSe .node ellipse,#mermaid-svg-05C2FWgRCuH0hVSe .node polygon,#mermaid-svg-05C2FWgRCuH0hVSe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-05C2FWgRCuH0hVSe .node .label{text-align:center;}#mermaid-svg-05C2FWgRCuH0hVSe .node.clickable{cursor:pointer;}#mermaid-svg-05C2FWgRCuH0hVSe .arrowheadPath{fill:#333333;}#mermaid-svg-05C2FWgRCuH0hVSe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-05C2FWgRCuH0hVSe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-05C2FWgRCuH0hVSe .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-05C2FWgRCuH0hVSe .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-05C2FWgRCuH0hVSe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-05C2FWgRCuH0hVSe .cluster text{fill:#333;}#mermaid-svg-05C2FWgRCuH0hVSe .cluster span{color:#333;}#mermaid-svg-05C2FWgRCuH0hVSe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-05C2FWgRCuH0hVSe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

请求推送文件到其他storage,
storage_sync_copy_file
检测文件是否已经被删除,
trunk_file_stat
文件不存在,
直接返回
目的storage是否已经有该文件,
storage_query_file_info_ex
文件已经存在且大小一致
不需要更新文件,
则不需要推送文件
文件已经存在但大小不一致
更新内容,
STORAGE_PROTO_CMD_SYNC_UPDATE_FILE
文件不存在
创建文件,
STORAGE_PROTO_CMD_SYNC_CREATE_FILE
封装协议,如果不需要更新文件则不用封装文件内容
发送协议的上半部,
tcpsenddata_nb
发送协议的下半部(文件内容),
tcpsenddata_ex
读取回应信息,
fdfs_recv_response

3.4.3、同步前删除

假如同步较为缓慢,那么有可能在开始同步一个文件之前,该文件已经被客户端删除,此时同步线程将打印一条日志,然后直接接着处理后面的Binlog。

3.5、Storage的最后最早被同步时间

举个例子:
一个group内有Storage-A、Storage-B、Storage-C三台机器。对于A这台机器来说,B与C机器都会同步Binlog(包括文件)给他,A在接收同步时会记录每台机器同步给他的最后时间(Binlog中的第一个字段timpstamp,这个时间也会更新到storage_stat.dat的last_sync_update)。比如B最后同步给A的Binlog-timestamp为100,C最后同步给A的Binlog-timestamp为200,那么A机器的最后最早被同步时间就为100。也就是取最小值。

这个值的意义在于,判断一个文件是否存在某个Storage上。
比如这里A机器的最后最早被同步时间为100,那么如果一个文件的创建时间为99,就可以肯定这个文件在A上肯定有。

Storage会定期将每台机器同步给他的最后时间告诉给Tracker,Tracker在客户端要下载一个文件时,需要判断一个Storage是否有该文件,只要解析文件的创建时间,然后与该值作比较,若该值大于创建创建时间,说明该Storage存在这个文件,可以从其下载。

Tracker也会定期将该值写入到一个文件之中,Storage_sync_timestamp.dat,内容如下:

group1,192.168.0.104,   0, 1408524351, 1408524352
group1,192.168.0.105,   1408524353, 0, 1408524354
group1,192.168.0.106,   1408524355, 1408524356, 0

每一行记录了对应Storage同步给其他Storage的最后时间。

机器 同步时间 同步时间 同步时间
group1,192.168.0.104 0 1408524351 1408524352
group1,192.168.0.105 1408524353 0 1408524354
group1,192.168.0.106 1408524355 1408524356, 0

如第一行含义:0表示自己同步给自己,没有记录;1408524351表示192.168.0.104同步给192.168.0.105的最后最早被同步时间;1408524352 表示192.168.0.104同步给192.168.0.106的最后最早被同步时间。其他行同理。

3.6 新增节点的同步流程

比如在已有A、B节点上,新增节点storage C。

#mermaid-svg-EUmuFYi55oEbsWiH {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-EUmuFYi55oEbsWiH .error-icon{fill:#552222;}#mermaid-svg-EUmuFYi55oEbsWiH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EUmuFYi55oEbsWiH .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-EUmuFYi55oEbsWiH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EUmuFYi55oEbsWiH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EUmuFYi55oEbsWiH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EUmuFYi55oEbsWiH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EUmuFYi55oEbsWiH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EUmuFYi55oEbsWiH .marker.cross{stroke:#333333;}#mermaid-svg-EUmuFYi55oEbsWiH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EUmuFYi55oEbsWiH .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EUmuFYi55oEbsWiH text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-EUmuFYi55oEbsWiH .actor-line{stroke:grey;}#mermaid-svg-EUmuFYi55oEbsWiH .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-EUmuFYi55oEbsWiH .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-EUmuFYi55oEbsWiH #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-EUmuFYi55oEbsWiH .sequenceNumber{fill:white;}#mermaid-svg-EUmuFYi55oEbsWiH #sequencenumber{fill:#333;}#mermaid-svg-EUmuFYi55oEbsWiH #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-EUmuFYi55oEbsWiH .messageText{fill:#333;stroke:#333;}#mermaid-svg-EUmuFYi55oEbsWiH .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EUmuFYi55oEbsWiH .labelText,#mermaid-svg-EUmuFYi55oEbsWiH .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-EUmuFYi55oEbsWiH .loopText,#mermaid-svg-EUmuFYi55oEbsWiH .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-EUmuFYi55oEbsWiH .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-EUmuFYi55oEbsWiH .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-EUmuFYi55oEbsWiH .noteText,#mermaid-svg-EUmuFYi55oEbsWiH .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-EUmuFYi55oEbsWiH .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EUmuFYi55oEbsWiH .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EUmuFYi55oEbsWiH .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EUmuFYi55oEbsWiH .actorPopupMenu{position:absolute;}#mermaid-svg-EUmuFYi55oEbsWiH .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-EUmuFYi55oEbsWiH .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EUmuFYi55oEbsWiH .actor-man circle,#mermaid-svg-EUmuFYi55oEbsWiH line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-EUmuFYi55oEbsWiH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}Storage CTrackerStorage AStorage B发送STORAGE_JOIN返回同组storage信息发送心跳返回同组storage list发现新增storage C,启动同步线程发送心跳返回同组storage list发现新增storage C,启动同步线程发现新增storage A和B,启动同步线程等待同步发送SYNC_SRC_REQ返回同步源IP及同步截至时间戳返回的源IP是自己,作为同步源准备同步binlog到C节点发送SYNC_SRC_REQ返回同步源IP及同步截至时间戳返回的源IP不是自己,等待storage C状态为Active时再同步。发送SYNC_DEST_REQ收到响应,状态设置为WAIT_SYNC请求将storage C的状态置为SYNCING返回响应读取binlog,解析数据发送命令同步数据返回同步结果binlog读取完,请求将storage C的状态置为OFFLINE返回响应同步完成,请求将storage C的状态置为ONLINE返回响应发送心跳状态设置ACTIVEStorage CTrackerStorage AStorage B
  1. 新节点storage C 启动的时候会创建线程tracker_report_thread_entrance,调用tracker_report_join向tracker 发送命令TRACKER_PROTO_CMD_STORAGE_JOIN(81)报告,自己的group名称,ip,端口,版本号,存储目录数,子目录数,启动时间,老数据是否同步完成,当前连接的tracker信息,当前状态信息(FDFS_STORAGE_STATUS_INIT)等信息。
  2. tracker收到TRACKER_PROTO_CMD_STORAGE_JOIN命令后,将上报的信息和已有(tracker数据文件中保存的信息)的信息进行比较,如果有则更新,没有的话,将节点及状态信息写入缓存和数据文件中,并查找同group的其他节点做为同步源,如果有返回给stroage C。
  3. 新节点stroage C 收到tracker响应继续流程。发送TRACKER_PROTO_CMD_STORAGE_SYNC_DEST_REQ (87)查询同步目的。
  4. tracker收到TRACKER_PROTO_CMD_STORAGE_SYNC_DEST_REQ 请求后, 查找同group的其他节点做为同步目标,及时间戳返回给新storage节点。
  5. 新storage节点收到响应后,保存同步源及同步时间戳。继续流程,发送TRACKER_PROTO_CMD_STORAGE_BEAT(83) 给tracker。
  6. tracker收到心跳报告后,leader trakcer(非leader不返回数据),把最新的group的 storagelist返回给新的stroaged。
  7. stroage C 收到 tracker storage list后,启动2个同步线程,准备将binlog同步到 节点 A和B(此时还不能同步,因为stroage C 还是WAIT_SYNC 状态)。
  8. 这时候,其他的已在线的storage 节点 A 、B会发送心跳给tracker ,tracker 把会收到最新的stroagelist,A、B、C返回给Storage A,B 。
  9. storage A,B 收到tracker响应后,会发现本地缓存中没有stroage C,会启动binlog同步线程,将数据同步给 stroage C。
  10. storage A 、B分别启动storage_sync_thread_entrance 同步线程,先向 tracker 发送TRACKER_PROTO_CMD_STORAGE_SYNC_SRC_REQ(86)命令,请求同步源,tracker会把同步源IP及同步时间戳返回。
  11. stroage A 、B节点的同步线程收到TRACKER_PROTO_CMD_STORAGE_SYNC_SRC_REQ 响应后,会检查返回的同步源IP是否和自己本地ip一致,如果一致置need_sync_old=1表示将做为源数据将老的数据,同步给新节点C,如果不一致置need_sync_old=0,则等待节点C状态为Active时,再同步(增量同步)。因为,如果A、 B同时作为同步源,同步数据给C的话,C数据会重复。这里假设节点A,判断tracker返回的是同步源和自己的iP一致,A做为同步源,将数据同步给storage C节点。
  12. Storage A同步线程继续同步流程,用同步目的的ip和端口,为文件名,.mark为后缀,如192.168.0.104_23000.mark,将同步信息写入此文件。将Storage C的状态置为FDFS_STORAGE_STATUS_SYNCING 上报给tracker,开始同步。

3.7、Tracker选择客户端下载文件的storage的原则

(1)在同group下,获取最小的一个同步时间点(各个storage在同一时间,同步完成的时间点不一样)。
(2)在最小同步时间点之前的文件,按照用户的规则随意选择一个storage。
(3)在最小同步时间点之后的文件,选择源storage提供给客户端。

总结

  1. fastdfs原理,要懂得逻辑闭环。
  2. binlog格式的设计,binlog文件有三列,依次为:时间戳,操作类型,文件ID(不带group名称)。默认binlog文件最大是1GB,如果超过1GB就创建一个新的binlog,可以是binlog001,binlog002,…;同时记录到.mark文件的binlog_index。
  3. 推送到不同的storage怎么记录?每个storage都有.mark文件,记录如下相关信息:
binlog_index=0      //binlog索引id 表示上次同步给其他机器的最后一条binlog文件索引
binlog_offset=3944 //当前时间binlog 大小 (单位是字节)表示上次同步给其他机器的最后一条binlog偏移量,若程序重启了,也只要从这个位置开始向后同步即可。
need_sync_old=1    //是否需要同步老数据
sync_old_done=1    //是否同步完成
until_timestamp=1621667115 //同步已有数据文件的截至时间
scan_row_count=68 //扫描记录数
sync_row_count=53 //同步记录数
  1. FastDFS做到高可用是tracker可以做集群,storage一般部署三个及以上。
  2. 写的高并发通过增加storage是不能提高并发的,因为storage直接需要推送文件;可以通过增加group、一个storage配置多个硬盘,每个硬盘对应一个store_path两种方式来提高写并发。
  3. 读的并发在少写多读的情况下可以通过扩充storage来提高并发,可以通过增加group,一个storage配置多个硬盘,每个硬盘对应一个store_path。
  4. 需要小文件存储的原因是小文件数量太多查找麻烦,而且因为inode的存在小文件会降低磁盘的利用率。
  5. fastdfs是弱一致性,如果是需要特别安全和特别可靠的就最好不要使用fastdfs。
  6. 两个storage直接的备份通过大小写字符来区别源数据和备份数据;如果是大写(比如C)就需要推送到其他storage,如果是小写(比如c)就不需要推送。从而避免备份死循环问题。
  7. 已经存在两个storage了,然后加入第三个storage,那么由Tracker决定由谁将数据同步到新的storage中,一般是选择最后最早同步时间戳的storage ,即取最小原则。
  8. Tracker选择客户端下载文件的storage的原则是在最小同步时间点之前的文件按照用户的规则随意选择一个,在最小同步时间点之后的文件,选择源storage提高给客户端。

FastDFS文件同步机制分析相关推荐

  1. ExoPlayer播放器剖析(六)ExoPlayer同步机制分析

    关联博客 ExoPlayer播放器剖析(一)进入ExoPlayer的世界 ExoPlayer播放器剖析(二)编写exoplayer的demo ExoPlayer播放器剖析(三)流程分析-从build到 ...

  2. futex同步机制分析之一应用

    futex同步机制分析之一应用 一.多线程(进程)的同步机制 c++编程中最难的部分有哪些,估计绝大多数人都会首先提出来是多线程(进程)编程.为什么多线程编程难呢?一个主要的原因就是多线程的同步.在多 ...

  3. Redis 数据同步机制分析

    Redis的主从同步机制可以确保redis的master和slave之间的数据同步.按照同步内容的多少可以分为全同步和部分同步:按照同步的时机可以分为slave刚启动时的初始化同步和正常运行过程中的数 ...

  4. VxWorks中信号量实现任务间通信与同步机制分析

    引 言 多任务内核.任务调度机制.任务间通信和中断处理机制,这些都是VxWorks运行环境的核心.多任务处理和任务间通信是实时操作系统的基石.一个多任务环境允许将一个实时应用构造成一套独立任务的集合, ...

  5. 游戏MMO技能系统的同步机制分析

    这里所说的技能系统包括:技能流程和技能创生体(法术场.弹道和buff). 首先介绍authority和proxy的概念,这两个概念是基于单位unit的基础上进行的区分. authority表示单位的主 ...

  6. futex同步机制分析之三内核实现

    一.源码引入 前两篇从应用分析到了库,本篇到内核中看看,futex到底何方神圣?(Linux3.1.1) 先看一下futex.c和futex.h(kennel/futex.c linux/futex. ...

  7. Sersync实现触发式文件同步

    序 言:如果我们后端有多台网站服务器或者文件服务器,而且没有好的文件同步机制,那么当我们升级程序或者更新文件的时候,就需要每台服务器或者目录都要更 新,这样很容易出问题,并很容易导致两边的文件不一致, ...

  8. 服务器文件同步异常检测,Sersync实现触发式文件同步

    目录 触发式文件同步 1:序言 如果我们后端有多台网站服务器或者文件服务器,而且没有好的文件同步机制,那么当我们升级程序或者更新文件的时候,就需要每台服务器或者目录都要更新,这样就很容易出问题,并很容 ...

  9. MySQL系列:innodb源代码分析之线程并发同步机制

    innodb是一个多线程并发的存储引擎,内部的读写都是用多线程来实现的,所以innodb内部实现了一个比較高效的并发同步机制. innodb并没有直接使用系统提供的锁(latch)同步结构,而是对其进 ...

最新文章

  1. web前端面试题(含答案)
  2. Segmentation fault (core dumped)
  3. node 获取表单数据 为空_数据结构与算法(python)单向链表篇
  4. hands-on Machine Learning with sklearn
  5. 盘点阿里程序员常用的 15 款开发者工具
  6. PropertyGrid 控件使用方法
  7. android 应用基础知识(2)---应用资源
  8. 图解Linux内核:内核启动(1)从Bootloader到内核代码
  9. 【题解】洛谷P4158 [SCOI2009] 粉刷匠(DP)
  10. 【鲲鹏HCIA考试】随堂习题卷三
  11. 如何开发Android手表界面ui,20款神奇的UI智能手表界面设计欣赏
  12. Flume错误:Flume:Class path contains multiple SLF4J bindings
  13. 【python+selenium】自动登陆学校青果教务网
  14. draggable 总结
  15. oracle中alter index,oracle alter index rebuild online和alter index rebuild的區別
  16. Jquery实现即点即改
  17. git 新建分支 推送到远程 首次pull代码报错 git branch --set-upstream-to=origin/<branch>
  18. 科技公司将跳过IPO直接接受机构融资
  19. 下班前几分钟,逮到一个腾讯10年老测试开发,聊过之后彻底悟了...
  20. OPENGL ES 2.0 知识串讲 (4)——GLSL 语法(II)

热门文章

  1. 电脑可安装的超炫实用软件
  2. 微软Win11 Dev/Beta预览版22581.1(ni_release)发布
  3. 手机图片怎么生成二维码?手机如何创建二维码?
  4. xampp下载,踩得mysql的坑!!使用本机之前装入的mysql+idea配置PHP
  5. 计算机考研真题及答案(含选择题解析)
  6. 打造区块链行业数据聚合边框
  7. 透析《长安十二时辰》里的望楼,人类在唐朝就有5G愿望了?
  8. PX环境搭建编译px4_fmu-v2_default相关问题的解决方法
  9. TAGE-SC-L Branch Predictors
  10. C语言上学期整理(第3章)