FastFDS原理

来源:互联网 发布:软件技术学校 编辑:程序博客网 时间:2024/06/05 00:08

FastFDS简介

FastDFS是一个开源的轻量级分布式文件系统。它解决了大数据量存储和负载均衡等问题,FastDFS是为互联网应用量身定做的一套分布式文件存储系统,特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务,如相册网站、视频网站等等。对于互联网应用,和其他分布式文件系统相比,优势非常明显。出于简洁考虑,FastDFS没有对文件做分块存储,因此不太适合分布式计算场景。截至2014年,至少有25家公司在使用FastDFS,如UC、支付宝、京东、迅雷等。

特点

  • 纯C实现,支持Linux、FreeBSD等UNIX系统
  • 类google FS,不是通用的文件系统,只能通过专有API访问,不支持POSIX接口方式,不能mount,使用目前提供了C、Java和PHP API
  • 为互联网应用量身定做,解决大容量文件存储问题,追求高性能和高扩展性
  • FastDFS可以看做是基于文件的key value pair存储系统,称作分布式文件存储服务更为合适
  • 分组存储,灵活简洁
  • 对等结构,不存在单点
  • 文件ID由FastDFS生成,作为文件访问凭证。FastDFS不需要传统的name server
  • 和流行的web server无缝衔接,FastDFS已提供apache和nginx扩展模块
  • 大、中、小文件均可以很好支持,支持海量小文件存储
  • 支持相同文件内容只保存一份,节省存储空间
  • 存储服务器上可以保存文件附加属性
  • 支持多块磁盘,支持单盘数据恢复
  • 下载文件支持多线程方式,支持断点续传

FastDFS架构

FastDFS架构

Tracker server之间相互独立,不存在直接联系。客户端和Storage server主动连接Tracker server。Storage server主动向Tracker server报告其状态信息,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。Storage server会连接集群中所有的Tracker server,向他们报告自己的状态。Storage server启动一个单独的线程来完成对一台Tracker server的连接和定时报告。需要说明的是,一个组包含的Storage server不是通过配置文件设定的,而是通过Tracker server获取到的。
不同组的Storage server之间不会相互通信,同组内的Storage server之间会相互连接进行文件同步。Storage server采用binlog文件记录文件上传、删除等更新操作。binlog中只记录文件名,不记录文件内容。文件同步只在同组内的Storage server之间进行,采用push方式,即源头服务器同步给目标服务器。只有源头数据才需要同步,备份数据并不需要再次同步,否则就构成环路了。有个例外,就是新增加一台Storage server时,由已有的一台Storage server将已有的所有数据(包括源头数据和备份数据)同步给该新增服务器。
Storage server中由专门的线程根据binlog进行文件同步。为了最大程度地避免相互影响以及出于系统简洁性考虑,Storage server对组内除自己以外的每台服务器都会启动一个线程来进行文件同步。文件同步采用增量同步方式,系统记录已同步的位置(binlog文件偏移量)到标识文件中。标识文件名格式:{dest storage IP}_{port}.mark,例如:192.168.1.14_23000.mark。

和现有的类Google FS分布式文件系统相比,FastDFS的架构和设计理念有其独到之处,主要体现在轻量级、分组方式和对等结构三个方面。

1. 轻量级

FastDFS只有两个角色:Tracker server和Storage server。Tracker server作为中心结点,其主要作用是负载均衡和调度。Tracker server在内存中记录分组和Storage server的状态等信息,不记录文件索引信息,占用的内存量很少。另外,客户端(应用)和Storage server访问Tracker server时,Tracker server扫描内存中的分组和Storage server信息,然后给出应答。由此可以看出Tracker server非常轻量化,不会成为系统瓶颈。
FastDFS中的Storage server在其他文件系统中通常称作Trunk server或Data server。Storage server直接利用OS的文件系统存储文件。FastDFS不会对文件进行分块存储,客户端上传的文件和Storage server上的文件一一对应。
在FastDFS中,客户端上传文件时,文件ID不是由客户端指定,而是由Storage server生成后返回给客户端的。文件ID中包含了组名、文件相对路径和文件名,Storage server可以根据文件ID直接定位到文件。因此FastDFS集群中根本不需要存储文件索引信息,这是FastDFS比较轻量级的一个例证。

file-id 的文件名组成,
- Storage_id(ip的数值型)
- timestamp(创建时间)
- file_size(若原始值为32位则前面加入一个随机值填充,最终为64位)
- crc32(文件内容的检验码)

2. 分组方式

FastDFS采用了分组存储方式。集群由一个或多个组构成,集群存储总容量为集群中所有组的存储容量之和。一个组由一台或多台存储服务器组成,同组内的多台Storage server之间是互备关系,同组存储服务器上的文件是完全一致的。文件上传、下载、删除等操作可以在组内任意一台Storage server上进行。类似木桶短板效应,一个组的存储容量为该组内存储服务器容量最小的那个,由此可见组内存储服务器的软硬件配置最好是一致的。
采用分组存储方式的好处是灵活、可控性较强。比如上传文件时,可以由客户端直接指定上传到的组。一个分组的存储服务器访问压力较大时,可以在该组增加存储服务器来扩充服务能力(纵向扩容)。当系统容量不足时,可以增加组来扩充存储容量(横向扩容)。采用这样的分组存储方式,可以使用FastDFS对文件进行管理,使用主流的Web server如Apache、nginx等进行文件下载。

3. 对等结构

FastDFS集群中的Tracker server也可以有多台,Tracker server和Storage server均不存在单点问题。Tracker server之间是对等关系,组内的Storage server之间也是对等关系。传统的Master-Slave结构中的Master是单点,写操作仅针对Master。如果Master失效,需要将Slave提升为Master,实现逻辑会比较复杂。和Master-Slave结构相比,对等结构中所有结点的地位是相同的,每个结点都是Master,不存在单点问题。

FastDFS 原理

文件上传:

FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用。文件上传类型有3种:
1. upload:上传普通文件,包括主文件
2. upload_appender:上传appender文件,后续可以对其进行append操作【又用作断点续传】
3. upload_slave:上传从文件

sequenceDiagramstorage server-->>tracker server: 1.定时向tracker 上传状态信息client->>tracker server: 2. 上传连接请求tracker server->>tracker server: 3. 查询可用storagetracker server-->>client:4.返回信息client->>storage server:5. 上传文件storage server->>storage server:6.生成file-idstorage server->>storage server:7.将上传内容写入磁盘storage server-->>client:8.返回file-idclient->>client:9.存储文件信息
  1. 选择tracker server
    当集群中不止一个tracker server时,由于tracker之间是完全对等的关系,客户端在upload文件时可以任意选择一个trakcer。
  2. 选择存储的group
    当tracker接收到upload file的请求时,会为该文件分配一个可以存储该文件的group,支持如下选择group的规则:
    1. Round robin,所有的group间轮询
    2. Specified group,指定某一个确定的group
    3. Load balance,剩余存储空间多多group优先
  3. 选择storage server
    当选定group后,tracker会在group内选择一个storage server给客户端,支持如下选择storage的规则:
    1. Round robin,在group内的所有storage间轮询
    2. First server ordered by ip,按ip排序
    3. First server ordered by priority,按优先级排序(优先级在storage上配置)
  4. 选择storage path
    当分配好storage server后,客户端将向storage发送写文件请求,storage将会为文件分配一个数据存储目录,支持如下规则:
    1. Round robin,多个存储目录间轮询
    2. 剩余存储空间最多的优先
  5. 生成Fileid
    选定存储目录之后,storage会为文件生一个Fileid,由storage server ip、文件创建时间、文件大小、文件crc32和一个随机数拼接而成,然后将这个二进制串进行base64编码,转换为可打印的字符串。
  6. 选择两级目录
    当选定存储目录之后,storage会为文件分配一个fileid,每个存储目录下有两级256*256的子目录,storage会按文件fileid进行两次hash(猜测),路由到其中一个子目录,然后将文件以fileid为文件名存储到该子目录下。
  7. 生成文件名
    当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。

文件下载

客户端upload file成功后,会拿到一个storage生成的文件名,接下来客户端根据这个文件名即可访问到该文件。

sequenceDiagramstorage server-->>tracker server: 1.定时向tracker 上传状态信息client->>tracker server: 2. 下载连接请求tracker server->>tracker server: 3. 查询可用storagetracker server-->>client:4.返回信息client->>storage server:5. file-idstorage server->>storage server:6.查找文件storage server-->>client:7.返回file-content

跟upload file一样,在download file时客户端可以选择任意tracker server。client发送download请求给某个tracker,必须带上文件名信息,tracke从文件名中解析出文件的group、大小、创建时间等信息,然后为该请求选择一个storage用来服务读请求。由于group内的文件同步时在后台异步进行的,所以有可能出现在读到时候,文件还没有同步到某些storage server上,为了尽量避免访问到这样的storage,tracker按照如下规则选择group内可读的storage。
1. 该文件上传到的源头storage
源头storage只要存活着,肯定包含这个文件,源头的地址被编码在文件名中。
2. 文件创建时间戳==storage被同步到的时间戳 且(当前时间-文件创建时间戳) > 文件同步最大时间(如5分钟)
文件创建后,认为经过最大同步时间后,肯定已经同步到其他storage了。
3. 文件创建时间戳 < storage被同步到的时间戳。
同步时间戳之前的文件确定已经同步了
4. (当前时间-文件创建时间戳) > 同步延迟阀值(如一天)。
经过同步延迟阈值时间,认为文件肯定已经同步了。

Storage上的Nginx Module首先会去看本机有没有被请求的文件,如果没有的话,会从FileId中解开源Storage的IP地址,然后去访问,如果此时源Storage当机了,那么本次下载请求就此失败(Nginx Module从始至终没有拿着被请求文件的Group Name去问Tracker现在哪台活着的Storage能响应请求)

文件删除

删除处理流程与文件下载类是:
1. Client询问Tracker server可以下载指定文件的Storage server,参数为文件ID(包含组名和文件名);
2. Tracker server返回一台可用的Storage server;
3. Client直接和该Storage server建立连接,完成文件删除。

文件同步

写文件时,客户端将文件写至group内一个storage server即认为写文件成功,storage server写完文件后,会由后台线程将文件同步至同group内其他的storage server。
每个storage写文件后,同时会写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内所有server的时钟保持同步。
storage的同步进度会作为元数据的一部分汇报到tracker上,tracke在选择读storage的时候会以同步进度作为参考。

断点续传

提供appender file的支持,通过upload_appender_file接口存储,appender file允许在创建后,对该文件进行append操作。实际上,appender file与普通文件的存储方式是相同的,不同的是,appender file不能被合并存储到trunk file。.续传涉及到的文件大小MD5不会改变。续传流程与文件上传类是,先定位到源storage,完成完整或部分上传,再通过binlog进行同group内server文件同步。

文件属性

FastDFS提供了设置/获取文件扩展属性的接口(setmeta/getmeta),扩展属性以key-value对的方式存储在storage上的同名文件(拥有特殊的前缀或后缀),比如/group/M00/00/01/some_file为原始文件,则该文件的扩展属性存储在/group/M00/00/01/.some_file.meta文件(真实情况不一定是这样,但机制类似),这样根据文件名就能定位到存储扩展属性的文件。
以上两个接口作者不建议使用,额外的meta文件会进一步“放大”海量小文件存储问题,同时由于meta非常小,其存储空间利用率也不高,比如100bytes的meta文件也需要占用4K(block_size)的存储空间。

HTTP访问支持

FastDFS的tracker和storage都内置了http协议的支持,客户端可以通过http协议来下载文件,tracker在接收到请求时,通过http的redirect机制将请求重定向至文件所在的storage上;除了内置的http协议外,FastDFS还提供了通过apache或nginx扩展模块下载文件的支持。

FastDFS 具体技术

Binlog同步

FastDFS为了维护文件的多个副本,会在同组的Storage之间互相同步文件,也就是一个备份过程,若一组有三台机器,那么互相备份后,一个文件就有三个副本。

  1. 目录结构

    在Storage.conf配置文件中,有一个base_path配置,在Storaged程序启动时会创建一个 base_path/data/sync 目录,该目录中的文件都是和Storaged之间的同步相关的,一般包含三个文件:

    • IP_port.mark
    • binlog.000
    • binglog.index

    binglog.index 记录当前使用的Binlog文件序号,如为1,则表示使用binlog.001

    IP_port.mark 同步状态文件,记录本机到IP机器的同步状态,文件主要的内容有两项:

    • binlog_index=0表示上次同步给IP机器的最后一条binlog文件索引;
    • binlog_offset=XXX表示上次同步给IP机器的最后一条binlog偏移量,若程序重启了,也只要从这个位置开始向后同步即可。

    其他的字段:

    • need_sync_old=0/1 是否需要同步旧文件给对方
    • sync_old_done=0/1 若需要同步旧文件,那么旧文件是否同步完成
    • until_timestamp=xxxxxxx 源主机负责的旧数据截止时间

    binlog.000 真实地Binlog文件,对于binlog.000文件,是有一条条binlog日志组成的:

    1453687700 C M00/00/01/wKiQ0Valg5SAdKqaAADIAFUGYq460218191453687700 C M00/00/01/wKiQ0Valg5SAcpG3AADIAFUGYq463442991453687700 C M00/00/01/wKiQ0Valg5SAHGqaAADIAFUGYq40745815

    每一条记录都是使用空格符分成三个字段:

    • 1453687700 表示文件upload时间戳
    • 字母 表示文件的创建方式,C表示源创建、c表示副本创建、A表示源追加、a表示副本追加、D表示源删除、d表示副本删除、T表示源Truncate、t表示副本Truncate
    • 文件FileID
  2. 同步过程

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

    1. 获取组内的其他Storage信息,并启动同步线程;

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

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

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

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

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

  3. Storage的最后最早被同步时间

    假设一组内有Storage-A、Storage-B、Storage-C三台机器。对于A这台机器来说,B与C机器都会同步Binlog(包括文件)给他,A在接受同步时会记录每台机器同步给他的最后时间(也就是Binlog中的第一个字段timpstamp)。比如B最后同步给A的Binlog-timestamp为100,C最后同步给A的Binlog-timestamp为200,那么A机器的最后最早被同步时间就为100.

    这个值的意义在于,判断一个文件是否存在某个Storage上。比如这里A机器的最后最早被同步时间为100,那么如果一个文件的创建时间为99,就可以肯定这个文件在A上肯定有。为什呢?一个文件会Upload到组内三台机器的任何一台上:
    1. 若这个文件是直接Upload到A上,那么A肯定有。
    2. 若这个文件是Upload到B上,由于B同步给A的最后时间为100,也就是说在100之前的文件都已经同步A了,那么A肯定有。
    3. 同理C也一样。

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

磁盘恢复

磁盘恢复也就是一台服务器的某个磁盘坏掉,换了一个新的硬盘,然后将旧磁盘本应该有的数据拷贝到新硬盘的过程。FastDFS推荐的方式是一个磁盘挂载成一个Store-Path,换了一个新的磁盘后,该Store_path目录下的数据丢失了,但是这部分数据在同组的其他Storage都有,因此只要从对应的Storage上拷贝到目录下的数据即可。

FastDFS-Storaged(storage_disk_recovery.c)程序在启动时会检查每个Store_path目录下的子目录个数,默认情况下,每个Store_path下面会创建两级256个子目录。当非首次启动时,发现某个Store_path下没有任何目录,则会进入磁盘恢复过程。

磁盘恢复过程

磁盘恢复过程包括三个步骤:RecoveryStart、RecoveryStore、RecoveryFinish;这三个步骤之中的RecoveryStore可以多次执行,因此在恢复过程中记录了中间状态,因此即使在磁盘恢复过程中宕机或停止,下次再次启动时还是可以从上次的状态继续恢复数据。

磁盘恢复过程的两个状态文件: .recovery.mark,.binlog.recovery。这两个状态文件保存与StorePath目录下,用于记录恢复过程的状态。

.recovery.mark文件包括三个字段:
- saved_storage_status= ##表示状态
- binlog_offset= ##表示恢复的binlog偏移量
- fetch_binlog_done= ##表示是否已经下载binlog

.binlog.recovery文件,记录的是需要恢复的文件binlog,格式同binlog.000 相同

  1. 磁盘恢复开始RecoveryStart

    在Storaged启动时,会依次检查每一个StorePath,首次启动时会为每个StorePath创建两级256个子目录(默认256,可配置),因此当非首次启动时,检测到StorePath下不存在这两级的256个子目录,那么程序就会认为该StorePath数据丢失,开始进行这个StorePath的磁盘恢复。
    该过程在storage_disk_recovery_start函数之中。
    RecoveryStart执行如下操作:

    1. 创建状态临时文件 .recovery.mark;.binlog.recovery,并初始化。
    2. 连接Tracker,查询该Storage的最大状态值(该值用于在磁盘恢复完成后,恢复到之前的状态),查询所在组的Storage列表,从中获取一个状态为Active的Storage,若获取不到Active的Storage则睡眠5秒后再次尝试;获取到的该Storage我们称为源Storage。
    3. 向Tracker报告,将自己状态设置成RECOVERY。
    4. 向源Storage发送FETCH_ONE_PATH_BINLOG命令,参数为group_name、store_path_index;源Storage收到该命令后从头遍历整个binlog,过滤出符合下列条件的binlog返回来:binlog文件的目录为指定的store_path;该binlog的操作类型为Create方式;该binlog对应的文件在文件系统中还存在;
    5. 将从源Storage返回的binlog写入到.binlog.recovery文件之中,若完成收到源Storage的整个回复,则设置.recovery.mark文件中的fetch_binlog_done标志为1;表示已经完成binlog文件的获取,设置saved_storage_status为第二步中查询到的最大状态值。
  2. 磁盘恢复下载文件

    这个步骤也就是真实地从一个源Storage上下载文件。详见storage_disk_recovery_store函数。如下:

    1. 检查是否存在.recovery.mark, .binlog.recovery文件,若任何一个不存在则返回
    2. 向RecoveryStart一样,向Tracker请求,查找该组内的一个状态为Active的Storage作为源Storage。
    3. 打开.recovery.mark 文件,读取binlog_offset值,打开.binlog.recovery文件,定位到binlog_offset这个位置,也就是从上次最后保存的状态开始继续下载文件
    4. 依次读取.binlog.recovery文件的后面记录,对于每一条记录都从源Storage中去下载对应文件
    5. 每下载1000个文件,将状态值binlog_offset写入到.recovery.mark 文件之中
    6. 当所有的binlog对应文件都下载完成后,向所有Tracker报告恢复之前的最高状态。
  3. 磁盘恢复结束

    该步骤清理开始时创建的临时文件 .recovery.mark, .binlog.recovery

Storage程序框架

FastDFS绝大多数功能都是在Storage中实现,包括网络处理、文件上传、下载、同步、磁盘恢复等功能。

启动过程

源码在fdfs_storaged.c/main,也是程序的主线程。此处将列举启动的主要过程,并非一个完整的操作列表。
1. 首先从配置文件中读取base_path配置,初始化日志,响应stop、restart命令;(Fdfs_storaged.c: 112)
然后将程序转入后台;(Fdfs_storaged.c: 147) 没有看过以前的版本,读别人的博文转入后台操作还在后边,此处流程似乎和以前的不一样。
2. 读取完整的配置文件内容,包括日志目录,各种线程个数,TrackerIP地址等各种信息,记录在各个全局变量之中;(Fdfs_storaged.c: 151)

**memo:**

* tracker_server can ocur more than once, and tracker_server format is “host:port”, host can be hostname or ip address.
* 决定文件是否可以重复的配置:check_file_duplicate,默认可重复,when set to true, must work with FastDHT server, more detail please see INSTALL of FastDHT. FastDHT download page: http://code.google.com/p/fastdht/downloads/list
* key_namespace: FastDHT key namespace, can’t be empty when check_file_duplicate is true. the key namespace should short as possible
* 源码中的设定:最多tracker 16 Tracker ip 不能是127.0.0.1
3. 执行创建或检查各store_path/data目录(storage_func.c:1869)
对于首次启动则依据配置创建data下面的二级子目录(默认256个二级子目录)
对于非首次启动则检查各store_path/data/下面的子目录是否存在,若不存在则进入磁盘恢复操作。(storage_func.c:845)
4. 连接Tracker,向其查询各种配置(主要是合并存储的配置),若有多个Tracker则只要从其中一个查询成功即可,因此Tracker的配置应该是一样的。(storage_func.c:1877)
5. 启动服务的监听端口,默认为23000,并设置该socket的超时时间。(Fdfs_storaged.c:159)
6. 写pid文件(Fdfs_storaged.c:174)
7. 文件同步的初始化(主要是创建 /base/data/sync/目录,读取binlog.index文件,打开对应的binlog.xxx文件,并seek到END)。
8. 初始化网络操作,预先分配一些客户端的缓冲区,创建网络线程,线程个数在配置文件中配置,默认为4个。
9. 处理各种信号,包括SIGPIPE等
10. 根据storage.conf中Tracker-Server的配置,为每个Tracker启动一个线程处理与其的通讯。
11. 设置定期执行的任务,并启动执行这些任务的线程
12. 进入网络监听的大循环,也就是Accept线程,如果配置了多个Accept线程,那么会开启N-1个线程执行同样地操作,因为主线程也执行该操作:
13. 此时监听大循环已经退出,进行程序的清理操作,包括把fsync日志,停掉各种线程,删除日志文件等操作。

0 0
原创粉丝点击