• 周五. 11 月 8th, 2024

    FastDFS海量小文件存储解决之道

    root

    5 月 9, 2022 #FastDFS

    作者:vivo 互联网服务器团队 – Zhou Changqing

    一、FastDFS 原理介绍

    FastDFS 是一个 C 语言实现的开源轻量级分布式文件系统 。

    支持 Linux、FreeBSD、AID 等 Unix 系统,解决了大容量的文件存储和高并发访问问题,文件存取实现了负载均衡,适合存储 4KB~500MB 之间的小文件,特别适合以文件为载体的在线服务,如图片、视频、文档等等。

    二、FastDFS 架构

    FastDFS 由三个部分构成:

    • 客户端(Client)
    • 跟踪服务器(TrackerServer)
    • 存储服务器(StorageServer)

    2.1 Tracker Server (跟踪服务器)

    Tracker Server (跟踪服务器) 主要是做调度工作,起到负载均衡的作用。

    **(1)【服务注册】** 管理 StorageServer 存储集群,StorageServer 启动时,会把自己注册到 TrackerServer 上,并且定期报告自身状态信息,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。

    **(2)【服务发现】**Client 访问 StorageServer 之前,必须先访问 TrackerServer,动态获取到 StorageServer 的连接信息,最终数据是和一个可用的 StorageServer 进行传输。

    (3)【负载均衡】

    • store group 分配策略:
    • 0:轮询方式
    • 1:指定组
    • 2:平衡负载 (选择最大剩余空间的组 (卷) 上传)
    • store server 分配策略:
    • 0:轮询方式
    • 1:根据 IP 地址进行排序选择第一个服务器( IP 地址最小者)
    • 2:根据优先级进行排序(上传优先级由 storage server 来设置,参数名为 upload_priority)
    • stroe path 分配 :
    • 0:轮流方式,多个目录依次存放文件
    • 2:选择剩余空间最大的目录存放文件(注意:剩余磁盘空间是动态的,因此存储到的目录或磁盘可能也是变化的)

    2.2 Tracker Server (跟踪服务器)

    Tracker Server (跟踪服务器) 主要提供容量和备份服务。

    **【分组管理】** 以 Group 为单位,每个 Group 包含多台 Storage Server,数据互为备份,存储容量以 Group 内容量最小的 storage 为准,已 Group 为单位组织存储方便应用隔离、负载均衡和副本数据定制。

    ** 缺点:**Group 容量受单机存储容量的限制,数据恢复只能依赖 Group 其他机器重新同步。

    **【数据同步】** 文件同步只能在 Group 内的 Storage Server 之间进行,采用 push 方式,即源服务器同步给目标服务器。源服务器读取 binlog 文件,将文件内容解析后,按操作命令发送给目标服务器,有目标服务按命令进行操作。

    三、上传下载流程

    3.1 上传流程解析

    3.1.1 选择 Tracker Server

    集群中的 tracker 之间都是对等的,客户端在上传文件时可任意选择一个 tracker 即可。

    3.1.2 分配 Group、Stroage Server 和 storage path(磁盘或者挂载点)

    tracker 接收到上传请求时会先给该文件分配一个可以存储的 Group ,然后在 Group 中分配一个 Storage Server 给客户端,最后在接收到客户端写文件请求时,Storage Server 会分配一个数据存储目录并写入。

    (该过程中的分配策略详见:【负载均衡】)

    3.1.3 生成 file_id 写入并返回

    Storage 会生成一个 file_id 来作为当前文件名,file_id 采用 base64 编码,包含:源 storage server ip、文件创建时间、文件大小、文件 CRC32 校验码 和 随机数。每个存储目录下 有两个 256*256 个子目录。

    Storage 会根据 file_id 进行两次 hash 路由到其中一个子目录中。

    最后以 file_id 为文件名存储文件到该子目录下并返回文件路径给客户端。

    最终文件存储路径:

    ** 分组 | 磁盘 | 子目录 | 文件名 **

    group1/M00/00/89/eQ6h3FKJf_PRl8p4AUz4wO8tqaA688.apk

    • 【分组】:文件上传时分配 Group。
    • 【磁盘路径】:存储服务器配置的虚拟路径,对应配置参数 store_path 例如:M00 对应 store_path0,M01 对应 store_path1。
    • 【两级目录】:存储服务器在每个虚拟磁盘路径下创建的两级目录,用于存储文件。

    3.2  下载流程解析

    3.2.1 解析路径并路由

    tracker 接收 client 发送的下载请求时,tracker 从文件名中解析出 Group、大小、创建时间等信息,然后根据 Group 选择一个 storage server 返回。

    3.2.2 校验读取并返回

    客户端和 Storage Server 建立链接,校验文件是否存在,最终返回文件数据。

    ** 缺点:**Group 之间文件同步是异步进行的,可能上传的文件还未同步到当前访问的 Storage Server 这台机器上或者延迟原因,将导致下载文件出现 404。所以引入 nginx_fastdfs_module 可以很好的解决同步和延迟问题。

    3.3 引入 fastdfs_nginx_module 组件后的下载架构

    FastDFS Nginx Module 功能介绍

    (1)【防盗链检查】

    利用 FastDFS nginx 扩展功能动态生成 token,设置 http.conf 配置。

    • 开启防盗链功能
    • http.default_content_type =
    • application/octet-stream
    • http.mime_types_filename=mime.types
    • 开启 token 防盗链功能
    • http.anti_steal.check_token=true
    • token 过期时间
    • http.anti_steal.token_ttl=900
    • 密钥
    • http.anti_steal.secret_key=xxx
    • token 过期后返回的内容
    • http.anti_steal.token_check_fail=/etc/fdfs/anti-steal.jpg

    【token 生成算法】:md5 (fileid_without_group + privKey + ts) 同时 ts 没有超过 ttl 范围。

    服务器会自动根据 token,st 以及设置的秘钥来验证合法性。访问链接形式如:

    http://localhost/G1/M00/00/01/wKgBD01c15nvKU1cAABAOeCdFS466570.jpg?token=b32cd06a53dea4376e43d71cc882f9cb&ts=1297930137

    (2)【文件元数据解析】

    根据 file_id 获取元数据信息,包括:源 storage ip,文件路径,名称,大小 等。

    (3)【文件访问路由】

    因文件的 file_Id 包含了上传文件时的源 Storage Server IP ,所以在获取不到本机下的文件时(未同步或者延迟情况下)FastDFS 扩展组件,会根据源服务器 IP 来重定向或者代理方式获取文件。

    • 重定向模式

    配置项 response_mode = redirect,服务器返回 302,重定向 url

    http:// 源 storage ip:port / 文件路径?redirect=1

    • 代理模式

    配置项 response_mode = proxy,使用源 storage 地址作为代理 proxy 的 host,其他部分不变

    四、同步机制

    4.1 同步规则

    同步只发生在本组的 Storage Server 之间。

    源头数据才需要同步,备份数据不需要再次同步。

    新增 Storage Server 时,会由已有一台 Storage Server 将已有的所有数据(源头数据和备份数据)同步给新增服务器。

    4.2 Binlog 复制

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

    时间戳 | 操作类型 | 文件名

    1490251373 C M02/52/CB/CtAqWVjTbm2AIqTkAAACd_nIZ7M797.jpg

    操作类型(部分):

    • C 表示源创建、c 表示副本创建
    • A 表示源追加、a 表示副本追加
    • D 表示源删除、d 表示副本删除
    • . . . . . . .

    4.3 同步流程

    新增 Storage Server 后,组内其他 Storage Server 服务器会启动同步线程,在 tracker 的协调下向新增服务器发起全量和增量同步操作。

    (1)Storage C 启动后向 tracker 上报所属 group、ip、port、版本号、存储目录数、子目录数、启动时间、老数据是否同步完成,当前状态等信息。

    (2)tracker 收到 Storage C 加入申请请求后,更新本地 storage list,返回给 C,并适时同步给 A、B。

    (3)storage C 向 tracker 申请同步请求,响应后变更自身状态为 WAIT_SYNC。

    (4)storage A 和 B 在心跳周期内从同步到的新 storage list 发现没有 C,则启动同步线程,先向 tracker 发起同步申请(TRACKER_PROTO_CMD_STORAGE_SYNC_SRC_REQ),tracker 会把同步源 IP 级同步时间戳返回给 A 和 B, 如果源 IP 和自己本地 IP 一致,则标记自己作为同步源用来做老数据同步(全量同步源),如果不一致,则标记自己作为增量同步源(只有在 C 节点状态为 Active 时才同步)。该决策是由 tracker 选择产生的,不可 A、B 同时作为同步源,同时同步给 C。

    (5)同步源(假设是 storage A)以 .mark 为后缀的文件记录目标机器同步信息,并上报变更 storage C 状态为 SYNCING。

    (6)从 /data.sync 目录下读取 binlog.index 中的,binlog 文件 Id,binlog.000 读取逐行读取,进行解析.(详见上面 binlog 内格式) 发送数据给 storage C ,C 接收并保存。

    (7)数据同步过程中 storage C 的状态变更过程 OFFLINE->ONLINE->ACTIVE。ACTIVE 是最终状态,表示 storage C 已对外提供服务。

    五、文件存储

    5.1 LOSF 问题

    小文件存储(LOSF)面临的问题:

    • 本地文件系统 innode 梳理优先,存储小文件数量受限。
    • 目录层级和目录中文件数量会导致访问文件开销很大(IO 次数多)。
    • 小文件存储,备份和恢复效率低。

    针对小文件存储问题,FastDFS 提供了文件合并解决方案。FastDFS 默认创建大文件为 64M,大文件可以存储很多小文件,容纳一个小文件的空间叫 slot,solt 最小 256 字节,最大 16M。小于 256 字节当 256 字节存储,超过 16M 文件单独存储。

    5.2 存储方式

    **(1)【默认存储方式】** 未开启合并 ,FastDFS 生成的 file_id 和磁盘上实际存储的文件一一对应。

    **(2)【合并存储方式】** 多个 file_id 对应文件被存储成了一个大文件 。trunk 文件名格式:/fastdfs/data/00/000001 文件名从 1 开始递增。而生成的 file_id 更长,会新增 16 个字节额外内容用来保存偏移量等信息。

    如下:

    • 【file_size】:占用大文件的空间(注意按照最小 slot-256 字节进行对齐)
    • 【mtime】:文件修改时间
    • 【crc32】:文件内容的 crc32 码
    • 【formatted_ext_name】:文件扩展名
    • 【alloc_size】:文件大小与 size 相等
    • 【id】:大文件 ID 如 000001
    • 【offset】:文件内容在 trunk 文件中的偏移量
    • 【size】:文件大小。

    5.3 存储空间管理

    **(1)【Trunk Server】** 由 tracker leader 在一组 Storage Server 选择出来的,并通知给该组内所有 Storage Server,负责为该组内所有 upload 操作分配空间。

    **(2)【空闲平衡树】**trunk server 会为每个 store_path 构造一个空闲平衡树,相同大小的空闲块保存在链表中,每次上传请求时会到根据上传的文件大小到平衡树中查找获取大于或者接近的空闲块,然后从空闲块中分割出多余的作为新的空闲块,重新加入平衡树。如果找不到则会重建一个新的 trunk 文件,并加入到平衡树中。该分配过程即是一个维护空闲平衡树的过程。

    **(3)【Trunk Binlog】** 开启了合并存储后,Trunk Server 会多出一个 TrunkBinlog 同步。TrunkBinlog 记录了 TrunkServer 所有分配与回收的空闲块操作,并由 Trunk Server 同步给同组中其他 storage server。

    TrunkBinlog 格式如下:

    时间戳 | 操作类型 | store_path_index | sub_path_high| sub_path_low | file.id| offset | size 1410750754 A 0 0 0 1 0 67108864

    各字段含义如下:

    • file.id】:TrunkFile 文件名,比如 000001
    • 【offset】:在 TrunkFile 文件中的偏移量
    • 【size】:占用的大小,按照 slot 对齐

    六、文件去重

    FastDFS 不具备文件去重能力,必须引入 FastDHT 来配合完成。FastDHT 是一个键值对的高效分布式 hash 系统,底层采用 Berkeley DB 来做数据库持久化,同步方式使用 binlog 复制方式。在 FastDFS 去重场景中,对文件内容做 hash,然后判断文件是否一致。

    在文件上传成功后,查看 Storage 存储对应存储路径,会发现返回的是一个软链接,之后每次重复上传都是返回一个指向第一次上传的文件的软链接。也就保证了文件只保存了一份。

    (注意:FastDFS 不会返回原始文件的索引,返回的全部都是软链接,当所有的软链接都被删除的时候,原始文件也会从 FastDFS 中被删除)。

    七、总结

    FastDFS 真正意义上只是一个管理文件的系统(应用级文件系统),比如管理上传文件、图片等。并不像系统磁盘文件系统 NTFS 或者 FAT 等这种系统级文件系统。

    著作权归作者所有

    文章来源:https://my.oschina.net/vivotech/blog/5519663

    root

    相关文章