FastDFS-Nginx扩展模块源码分析

1. 背景

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

2. 概要介绍

关于FastDFS的架构原理不再赘述,有兴趣可以参考:http://code.google.com/p/fastdfs/wiki/Overview

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:
  1. 调用fdfs_load_tracker_group_ex解析tracker连接配置 ;
  2. 调用fdfs_get_ini_context_from_tracker连接tracker获取配置信息;
  3. 获取storage_sync_file_max_delay阈值
  4. 获取use_storage_id
  5. 如果use_storage_id为true,则连接tracker获取storage_ids映射表(调用方法:fdfs_get_storage_ids_from_tracker_group)
  • load_fdfs_parameters_from_tracker=false:
  1. 从mod_fastdfs.conf加载所需配置:storage_sync_file_max_delay、use_storage_id;
  2. 如果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,文件路径、名称,大小 
代码

    )...

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
  ...
        && 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获取本地文件信息,该方法将实现:

  1. 辨别当前文件是trunkfile还是singlefile
  2. 获得文件句柄fd
  3. 如果文件是trunk形式则同时也将相关信息(偏移量/长度)一并获得

代码

    if (bSameGroup)
    {
            FDFSTrunkHeader trunkHeader;
        if ((result=trunk_file_stat_ex1(pStorePaths, store_path_index, \
            true_filename, filename_len, &file_stat, \
            &trunkInfo, &trunkHeader, &fd)) != )
        {
            bFileExists = false;
        }
        else
        {
            bFileExists = true;
        }
    }
    else
    {
        bFileExists = false;
        memset(&trunkInfo, , sizeof(trunkInfo));
    }

3.3.5 文件不存在的处理

  • 进行有效性检查

检查项有二:

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

代码

     if (is_local_host_ip(file_info.source_ip_addr) || \
        (file_info.create_timestamp >  && (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方式循环读,输出... 
代码

    )
    {
        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 == ) ? : , file_trunk_buff, \
            read_bytes) != )
        {
            close(fd);
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    }

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

4. 扩展阅读

基于Referer实现防盗链: 
http://www.cnblogs.com/wJiang/archive/2010/04/04/1704445.html

FastDFS使用FAQ: 
http://bbs.chinaunix.net/thread-1920470-1-1.html

FastDFS-Nginx扩展的配置参考: 
http://blog.csdn.net/poechant/article/details/7036594

FastDFS配置、部署资料整理-CSDN博客: 
http://blog.csdn.net/poechant/article/details/6996047

关于C语言open和fopen区别 
http://blog.csdn.net/hairetz/article/details/4150193

fastdfs-nginx扩展模块源码分析的更多相关文章

  1. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  2. Nginx学习笔记(五) 源码分析&amp;内存模块&amp;内存对齐

    Nginx源码分析&内存模块 今天总结了下C语言的内存分配问题,那么就看看Nginx的内存分配相关模型的具体实现.还有内存对齐的内容~~不懂的可以看看~~ src/os/unix/Ngx_al ...

  3. Nginx学习笔记(四) 源码分析&amp;socket/UDP/shmem

    源码分析 在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem).那就来看看这个文件吧!从简单的开始~~~ src/os/unix/Ngx_socket.h&Ngx_s ...

  4. Django搭建及源码分析(三)---+uWSGI+nginx

    每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当 ...

  5. Nginx学习笔记4 源码分析

    Nginx学习笔记(四) 源码分析 源码分析 在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem).那就来看看这个文件吧!从简单的开始~~~ src/os/unix/Ngx_ ...

  6. nginx健康检查模块源码分析

    nginx健康检查模块 本文所说的nginx健康检查模块是指nginx_upstream_check_module模块.nginx_upstream_check_module模块是Taobao定制的用 ...

  7. 手机自动化测试:Appium源码分析之跟踪代码分析八

    手机自动化测试:Appium源码分析之跟踪代码分析八   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...

  8. [转]RMI方式Ehcache集群的源码分析

    RMI方式Ehcache集群的源码分析   Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群.如下图所示:   Ehcache支持 ...

  9. RMI方式Ehcache集群的源码分析

    Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群.如下图所示: Ehcache支持多种集群方式,下面以RMI通信方式为例,来具体分 ...

随机推荐

  1. 华硕win10文档类文件点击右键时会闪一下,没法用右键打开文件

    华硕的win10系统,把系统自带的福昕软件Foxit PhantomPDF卸载了就好了

  2. thinphp框架的项目svn重新检出后的必备配置

    刚刚试着去了解thinkphp框架,在这里做一些笔记,后续有新的总结会更新到这里,如有错误与遗漏,望大家指正. 用thinkphp框架的项目,在用svn重新检出之后,需要进行一些基本配置,方可在本地打 ...

  3. java技术 spring 配置

    spring 的IOC是反射注入,用来管理对象的创建与销毁.一般使用都是在启动的web服务器的时候就创建了对象,可以选择自动装配对象管理,将对象引用实现与引用分开.采用的xml配置方式.及大减少了各个 ...

  4. 理解模板引擎Razor 的原理

    Razor是ASP.NET MVC 3中新加入的技术,以作为ASPX引擎的一个新的替代项.简洁的语法与.NET Framework 结合,广泛应用于ASP.NET MVC 项目.Razor Pad是一 ...

  5. iOS 滑动隐藏导航栏-三种方式

    /** 1隐藏导航栏-简单- */    self.navigationController.hidesBarsOnSwipe = YES; /** 2隐藏导航栏-不随tableView滑动消失效果 ...

  6. 如何使Python完美升级到新版本

    这里提供一种解决的方法 (加上一句话,发现一个新问题:这种方法yum update 后,需要将/usr/bin下的python文件删除,然后执行: # ln -s /usr/local/python2 ...

  7. Scala 深入浅出实战经典 第78讲:Type与Class实战详解

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  8. C#委托实例

    using System; using System.Collections.Generic; using System.Linq; using System.Text;   namespace we ...

  9. [51NOD1230]幸运数(数位DP)

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1230 dp(l,s,ss)表示长度为l的数各位和为s,各位平方 ...

  10. [JS] 限制上传文件的类型和大小

    <!DOCTYPE html> <!-- saved from url=(0035)http://localhost:9090/qraved/update --> <ht ...