FastDFS-Nginx扩展模块源码分析 -- mod_fastdfs原理解析

1. 背景
在大多数业务场景中,往往需要为FastDFS存储的文件提供http下载服务,而尽管FastDFS在其storage及tracker都内置了http服务, 但性能表现却不尽如人意;
作者余庆在后来的版本中增加了基于当前主流web服务器的扩展模块(包括nginx/apache),其用意在于利用web服务器直接对本机storage数据文件提供http服务,以提高文件下载的性能。


2. 概要介绍
关于FastDFS的架构原理不再赘述,有兴趣可以参考:http://blog.chinaunix.net/uid-20196318-id-4058561.html。


2.1 参考架构
使用FastDFS整合Nginx的参考架构如下所示




说明: 在每一台storage服务器主机上部署Nginx及FastDFS扩展模块,由Nginx模块对storage存储的文件提供http下载服务, 仅当当前storage节点找不到文件时会向源storage主机发起redirect或proxy动作。 
注:图中的tracker可能为多个tracker组成的集群;且当前FastDFS的Nginx扩展模块支持单机多个group的情况


 

2.2 几个概念
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认证的方式进行文件防盗链检查。


 


3. 实现原理
 3.1 源码包说明
下载后的源码包很小,仅包括以下文件:


 ngx_http_fastdfs_module.c   //nginx-module接口实现文件,用于接入fastdfs-module核心模块逻辑
 common.c                    //fastdfs-module核心模块,实现了初始化、文件下载的主要逻辑
 common.h                    //对应于common.c的头文件
 config                      //编译模块所用的配置,里面定义了一些重要的常量,如扩展配置文件路径、文件下载chunk大小
 mod_fastdfs.conf            //扩展配置文件的demo
 


3.2 初始化




3.2.1 加载配置文件
目标文件:/etc/fdfs/mod_fastdfs.conf


3.2.2 读取扩展模块配置
一些重要参数包括:


复制代码
      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下载服务端配置
复制代码


3.2.3 加载服务端配置
根据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_file_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_file_max_delay、use_storage_id;
如果use_storage_id为true,则根据storage_ids_filename获取storage_ids映射表(调用方法:fdfs_load_storage_ids_from_file)
 


3.3 下载过程




3.3.1 解析访问路径
    得到group和file_id_without_group两个参数;


 


 3.3.2 防盗链检查
根据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


 


3.3.3 获取文件元数据
根据文件ID 获取元数据信息, 包括:源storage ip,文件路径、名称,大小 
代码:


    if ((result=fdfs_get_file_info_ex1(file_id, false, &file_info)) != 0)...
在fdfs_get_file_info_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(见tracker_types.h)之间; 
其中FDFS_MAX_SERVER_ID = (1 << 24) - 1,该做法利用了ipv4地址的特点(由4*8个二进制位组成),即ipv4地址数值务必大于该阈值


3.3.4 检查本地文件是否存在
调用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;
        }
    }
    else
    {
        bFileExists = false;
        memset(&trunkInfo, 0, sizeof(trunkInfo));
    }

 





3.3.5 文件不存在的处理
进行有效性检查
检查项有二:


A. 源storage是本机或者当前时间与文件创建时间的差距已经超过阈值,报错;
代码:

 

 

 

 

     if (is_local_host_ip(file_info.source_ip_addr) || \
        (file_info.create_timestamp > 0 && (time(NULL) - \
            file_info.create_timestamp > '''storage_sync_file_max_delay''')))

 



 


B. 如果是redirect后的场景,同样报错;
如果是由其他storage节点redirect过来的请求,其url参数中会存在redirect一项


在通过有效性检查之后将进行代理或重定向处理


重定向模式
配置项response_mode = redirect,此时服务端返回返回302响应码,url如下:


http:// {源storage地址} : {当前port} {当前url} {参数"redirect=1"}(标记已重定向过)
 


代码:



   

 

   response.redirect_url_len = snprintf( \
                response.redirect_url, \
                sizeof(response.redirect_url), \
                "http://%s%s%s%s%c%s", \
                file_info.source_ip_addr, port_part, \
                path_split_str, url, \
                param_split_char, "redirect=1");

 

 


 


注:该模式下要求源storage配备公开访问的webserver、同样的端口(一般是80)、同样的path配置。


代理模式
配置项response_mode = proxy,该模式的工作原理如同反向代理的做法,而仅仅使用源storage地址作为代理proxy的host,其余部分保持不变。 
代码:

 

 

 

 

 

 

 

       if (pContext->proxy_handler != NULL)
		{
			return pContext->proxy_handler(pContext->arg, \
					file_info.source_ip_addr);
		}

 

 



        //其中proxy_handler方法来自ngx_http_fastdfs_module.c文件的ngx_http_fastdfs_proxy_handler方法
        //其实现中设置了大量回调、变量,并最终调用代理请求方法,返回结果:
        rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);  //执行代理请求,并返回结果
3.3.6 输出本地文件
当本地文件存在时,将直接输出。
根据是否trunkfile获取文件名,文件名长度、文件offset;
代码:



   

 

 

 bTrunkFile = IS_TRUNK_FILE_BY_ID(trunkInfo);
    if (bTrunkFile)
    {
        trunk_get_full_filename_ex(pStorePaths, &trunkInfo, \
                full_filename, sizeof(full_filename));
        full_filename_len = strlen(full_filename);
        file_offset = TRUNK_FILE_START_OFFSET(trunkInfo) + \
                pContext->range.start;
    }
    else
    {
        full_filename_len = snprintf(full_filename, \
                sizeof(full_filename), "%s/data/%s", \
                pStorePaths->paths[store_path_index], \
                true_filename);
        file_offset = pContext->range.start;
    }

 

 


 


若nginx开启了send_file开关而且当前为非chunkFile的情况下尝试使用sendfile方法以优化性能;
代码:

 

 

 

 

 

 

 

 

 

    if (pContext->send_file != NULL && !bTrunkFile)
    {
        http_status = pContext->if_range ? \
                HTTP_PARTIAL_CONTENT : HTTP_OK;
        OUTPUT_HEADERS(pContext, (&response), http_status)
        ......
        return pContext->send_file(pContext->arg, full_filename, \
                full_filename_len, file_offset, download_bytes);
    }

 

 

 




 


否则使用lseek 方式随机访问文件,并输出相应的段;
做法:使用chunk方式循环读,输出... 
代码:

 

 

 

 

 

 

 

 

    while (remain_bytes > 0)
    {
        read_bytes = remain_bytes <= FDFS_OUTPUT_CHUNK_SIZE ? \
                 remain_bytes : FDFS_OUTPUT_CHUNK_SIZE;
        if (read(fd, file_trunk_buff, read_bytes) != read_bytes)
        {
            close(fd);
            ......
            return HTTP_INTERNAL_SERVER_ERROR;
        }


        remain_bytes -= read_bytes;
        if (pContext->send_reply_chunk(pContext->arg, \
            (remain_bytes == 0) ? 1: 0, file_trunk_buff, \
            read_bytes) != 0)
        {
            close(fd);
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    }

 

 




 


其中chunk大小见config文件配置: -DFDFS_OUTPUT_CHUNK_SIZE='256*1024'

 

 

 

 

 

 

 

转载出处:http://www.cnblogs.com/littleatp/p/4361318.html。

 

技术交流

方式一:个人微信

CleverCode是一名架构师,技术交流,咨询问题,请加CleverCode个人微信号:clever_code

方式二:qq群

CleverCode是一名架构师,技术交流,咨询问题,请加CleverCode创建的qq群(架构师社区):517133582

<think>嗯,用户问的是如何安装fastdfs-nginx-module。首先,我需要回忆一下FastDFSNginx的关系。FastDFS是一个分布式文件系统,而fastdfs-nginx-module是它的一个扩展模块,用来让Nginx直接访问FastDFS中的文件,这样可以通过HTTP来提供文件服务。 接下来,我得考虑安装步骤的大致流程。通常,安装这类模块需要先安装FastDFS本身,然后再编译Nginx的时候加上这个模块。但可能用户已经安装了FastDFSNginx,或者需要从头开始安装。所以步骤应该包括依赖安装、下载源码、配置、编译安装等。 然后,我需要确认具体的步骤是否正确。比如,是否需要先安装FastDFS的依赖库,比如libfastcommon。然后下载fastdfs-nginx-module的源码,通常是从GitHub上获取。接着,在编译Nginx时,通过--add-module参数指定模块的路径。之后,还需要配置NginxFastDFS的相关配置文件,比如mod_fastdfs.conf,以及storage.conf中的HTTP设置。 另外,可能会遇到的一些常见问题,比如权限问题,路径是否正确,配置文件中的参数是否匹配tracker和storage的配置。还有,防火墙是否开放了相应的端口,比如80或者8080。此外,用户可能是在不同的Linux发行版上安装,可能需要调整包管理器的命令,比如CentOS用yum,Ubuntu用apt。 可能用户会遗漏的步骤包括:修改mod_fastdfs.conf中的tracker_server地址,或者没有正确设置storage的http.server_port,导致Nginx无法正确路由请求。此外,编译Nginx时,如果之前已经安装过Nginx,可能需要先卸载,或者备份有配置,避免覆盖。 还要注意模块的版本兼容性,确保fastdfs-nginx-module的版本与FastDFSNginx的版本兼容。可能需要查阅官方文档或者社区的建议。另外,在启动Nginx之前,是否已经启动了FastDFS的tracker和storage服务,否则Nginx可能无法正常连接到后端。 最后,测试部分也很重要,用户可能需要通过上传文件到FastDFS,然后通过Nginx的URL访问来验证是否安装成功。如果出现403 Forbidden,可能是文件权限问题,或者路径配置错误。 现在需要把这些步骤整理成清晰的中文指南,分步骤说明,确保用户能一步步跟着操作,同时提醒常见的配置错误和解决方法。可能还需要给出示例配置片段,帮助用户正确修改配置文件。如果有必要,可以建议用户查看日志文件,比如Nginx的error.log或FastDFS的日志,来排查问题。</think>安装fastdfs-nginx-module需要与NginxFastDFS配合使用,以下是详细步骤: ----- ### 一、准备工作 1. **安装依赖** ```bash # CentOS yum install -y gcc pcre pcre-devel openssl openssl-devel zlib zlib-devel # Ubuntu apt-get install build-essential libpcre3 libpcre3-dev libssl-dev zlib1g-dev ``` 2. **下载源码** ```bash wget https://github.com/happyfish100/fastdfs-nginx-module/archive/refs/tags/V1.22.tar.gz tar -zxvf V1.22.tar.gz ``` ----- ### 二、编译安装Nginx 1. **配置模块路径** ```bash ./configure \ --add-module=/path/to/fastdfs-nginx-module/src \ --with-http_ssl_module ``` 2. **编译并安装** ```bash make && make install ``` ----- ### 三、配置模块 1. **复制配置文件** ```bash cp /path/to/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/ ``` 2. **修改关键配置** ```conf # /etc/fdfs/mod_fastdfs.conf tracker_server=your_tracker_ip:22122 url_have_group_name=true store_path0=/path/to/fastdfs/storage/data ``` ----- ### 四、Nginx服务配置 1. **添加server配置** ```nginx server { listen 80; server_name localhost; location /group1/M00 { ngx_fastdfs_module; } } ``` 2. **重启服务** ```bash nginx -s stop && nginx ``` ----- ### 五、验证安装 1. **上传测试文件** ```bash /usr/bin/fdfs_test /etc/fdfs/client.conf upload test.txt ``` 2. **通过URL访问** ``` http://your_server_ip/group1/M00/00/00/[fileID] ``` ----- ### 常见问题排查 1. **文件访问404** - 检查`mod_fastdfs.conf`中`store_path0`是否与storage配置一致 - 确认Nginx用户对存储目录有读权限 2. **模块加载失败** ```bash ldd /usr/local/nginx/sbin/nginx | grep fastdfs ``` 出现`libfdfsclient.so`说明加载成功 3. **日志分** ```bash tail -f /usr/local/nginx/logs/error.log ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值