FastDFS学习笔记
FastDFS整合Nginx模块
背景
- 在大多数业务场景中,往往需要为 FastDFS 存储的文件提供 http下载服务,而尽管 FastDFS 在其 storage 及 tracker 都内置了 http 服务, 但性能表现却不尽如人意。(余老师在 V4.05 以后的版本就把内置的 HTTP 服务去掉了)。
- 作者余庆在后来的版本中增加了基于当前主流 web 服务器的扩展模块(包括nginx/apache),其用意在于利用 web 服务器直接对本机 storage 数据文件提供 http服务,以提高文件下载的性能。
- 其实不使用 Nginx 的扩展模块,只安装 web 服务器(Nginx 和 Apache),也可以对存储的文件进行访问。那么为什么要使用 Nginx 的扩展模块来访问存储的文件,原因有两个:
**1)**如果进行文件合并存储,那么不使用 FastDFS 的 nginx 扩展模块,是无法访问到合并后的文件的,因为文件合并之后,多个小文件都是存储在一个 trunk文件中的,在存储目录下,是看不到具体的小文件的。
**2)**如果文件未同步成功,那么不使用FastDFS的nginx扩展模块,是无法正常访问到指定的文件的,而使用了FastDFS的nginx扩展模块之后,如果要访问的文件未同步成功,那么会解析出来该文件的源存储服务器ip,然后将该访问请求重定向或者代理到源存储服务器中进行访问。
概要介绍
FastDFS整合Nginx的参考架构
- 说明:在每一台 storage 服务器主机上部署 Nginx 及 FastDFS 扩展模块,由 Nginx 模块对storage 存储的文件提供 http 下载服务,仅当当前 storage 节点找不到文件时会向源 storage 主机发起 redirect(重定向) 或 proxy(代理) 动作。
- 注意:图中的 tracker 可能为多个 tracker 组成的集群,且当前 FastDFS 的 Nginx 扩展模块支持单机多个 group 的情况。
几个概念
- storage_id:指 storage server 的 id,从 FastDFS4.x 版本开始,tracker可以对 storage 定义一组 ip 到 id 的映射,以 id 的形式对 storage进行管理。而文件名写入的不再是storage的ip而是id,这样的方式对于数据迁移十分有利。
- storage_sync_file_max_delay:指 storage 节点同步一个文件最大的时间延迟,是一个阈值;如果当前时间与文件创建时间的差距超过该值则认为同步已经完成。
- anti_steal_token:指文件ID防盗链的方式,FastDFS采用 token 认证的方式进行文件防盗链检查。
实现原理
源码包说明
下载后的源码包很小,仅包括以下文件:
- ngx_http_fastdfs_module.c:nginx-module 接口实现文件,用于接入 fastdfs-module 核心模块逻辑
- common.c:astdfs-module 核心模块,实现了初始化、文件下载的主要逻辑
- common.h:对应于 common.c 的头文件
- config:编译模块所用的配置,里面定义了一些重要的常量,如扩展配置文件路径、文件下载 chunk 大小
- mod_fastdfs.conf:扩展配置值文件的 demo
初始化
加载配置文件
目标文件:/etc/fdfs/mod_fastdfs.conf
读取扩展模块配置
一些重要参数包括:
- group_count:group个数
- url_have_group_name:url中是否包含group
- group.store_path:group对应的存储路径
- connect_timeout:连接超时
- network_timeout:接收或发送超时
- storage_server_port:storage_server端口,用于在找不到文件情况下连接源 storage 下载文件(该做法已过时)
- response_mode:响应模式,proxy 或 redirect
- load_fdfs_parameters_from_tracker:是否从 tracker 下载服务端配置
加载服务端配置
根据 load_fdfs_parameters_from_tracker 参数确定是否从 tracker 获取 server 端的配置信息
- load_fdfs_parameters_from_tracker=true:
* 调用fdfs_load_tracker_group_ex解析tracker连接配置;
* 调用fdfs_get_ini_context_from_tracker连接tracker获取配置信息;
* 获取storage_sync_le_max_delay阈值;
* 获取use_storage_id
* 如果use_storage_id为true,则连接tracker获取storage_ids映射表(调用方法:fdfs_get_storage_ids_from_tracker_group)
- load_fdfs_parameters_from_tracker=false:
* 从mod_fastdfs.conf加载所需配置:storage_sync_le_max_delay、use_storage_id;
* 如果use_storage_id为true,则根据storage_ids_lename获取storage_ids映射表(调用法:fdfs_load_storage_ids_from_file)
下载过程(重点)
解析访问路径
得到 group 和 file_id_without_group 两个参数
防盗链检查
- 根据 g_http_params.anti_steal_token 配置(见 http.conf 文件),判断是否进行防盗链检查。
- 采用 token 的方式实现防盗链,该方式要求下载地址带上 token,且 token 具有时效性(由 ts 参数指明)。
- 检查方式:md5(fileid_without_group + privKey + ts) = token;同时 ts 没有超过 ttl 范围(可参考JavaClient CommonProtocol)。
- 调用方法:fdfs_http_check_token 关于 FastDFS 的防盗链可参考:http://bbs.chinaunix.net/thread-1916999-1-1.html
获取文件元数据
根据文件ID 获取元数据信息, 包括:源 storage ip,文件路径、名称、大小,代码如下:
if((result=fdfs_get_file_info_ex1(file_id, false, &file_info)) != 0) {
... }
在 fdfs_get_file_into_ex1 的实现中,存在一个取巧的逻辑:当获得文件的 ip 段之后,仍然需要确定该段落是 storage 的 id 还是 ip,代码如下:
fdfs_shared.func.c
-> fdfs_get_server_id_type(ip_addr.s_addr) == FDFS_ID_TYPE_SERVER_ID
...
if(id>0 && id <= FDFS_MAX_SERVER_ID) {
return FDFS_ID_TYPE_SERVER_ID;
} else {
return FDFS_ID_TYPE_IP_ADDRESS;
}
判断标准为ip段的整数值是否在 0 到 FDFS_MAX_SERVER_ID 之间:其中 FDFS_MAX_SERVER_ID = (1 << 24) - 1,该做法利用了 IPV4 地址的特点(由 4*8 个二进制位组成),即 IPV4 地址数值务必大于该阈值。
检查本地文件是否存在
调用 trunk_file_stat_ex1 获取本地文件信息,该方法将实现:
- 辨别当前文件是 trunkfile 还是 singlefile
- 获取文件句柄fd
- 如果文件时 trunk 形式则同时也将相关信息(偏移量/长度)一并获得
代码如下:
if(bSameGroup) {
FDFSTrunkHeader trunkHeader;
if((result=trunk_file_stat_ex1(pStorePaths, store_path_index, true_filename, filename_len, &file_stat, &trunkInfo, &trunkHeader, &fd)) != 0){
bFileExists = false;
} else {
bFileExists = true;
}