C/C++编程:FastDFS-Nginx扩展模块分析
发布日期:2022-03-16 03:25:44 浏览次数:31 分类:技术文章

本文共 5648 字,大约阅读时间需要 18 分钟。

架构

使用FastDFS整合Nginx的架构如下图:

在这里插入图片描述
说明:

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

实现原理

源码包说明

  • ngx_http_fastdfs_module.c:nginx-module接口实现文件,用于接入fastdfs-module核心模块逻辑
  • common.c :fastdfs-module核心模块,实现了初始化、文件下载的主要逻辑
  • config:/编译模块所⽤的配置,⾥⾯定义了⼀些重要的常量,如扩展配置⽂件路径、⽂件下载chunk⼤⼩
  • mod_fastdfs.conf:扩展配置⽂件的范例

初始化

在这里插入图片描述

加载配置⽂件

⽬标⽂件:/etc/fdfs/mod_fastdfs.conf

读取扩展模块配置

⼀些重要参数包括:

group_count           //group个数url_have_group_name   //url中是否包含groupgroup.store_path      //group对应的存储路径connect_timeout       //连接超时network_timeout       //接收或发送超时storage_server_port   //storage_server端⼝,⽤于在找不到⽂件情况下连接源storage下载⽂件(该做法已过时)response_mode         //响应模式,proxy或redirectload_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_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)

下载过程

在这里插入图片描述

解析访问路径

得到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范围 (可参考JavaClientCommonProtocol)
    • 调⽤⽅法:fdfs_http_check_token

获取文件元数据

根据文件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地址数值务必⼤于该阈值

检查本地⽂件是否存在

调⽤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)) != 0) {
bFileExists = false; } else {
bFileExists = true; }}else{
bFileExists = false; memset(&trunkInfo, 0, sizeof(trunkInfo));}

⽂件不存在的处理

进行有效性检查

检查项有二。

1) 源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''')))

2)如果是redirect后的场景,同样报错(如果是由其他storage节点redirect过来的请求,其url参数中会存在redirect⼀项)。

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

代理或者重定向处理

1)重定向模式

配置项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配置。

2)代理模式

配置项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); //执⾏代理请求,并返回结果

在这里插入图片描述

本地文件存在时直接输出

1)根据是否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;}

2)若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);}

3)否则使⽤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’。

转载地址:https://blog.csdn.net/zhizhengguan/article/details/123368499 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:网络:数据包、报文、帧、分组、段
下一篇:gdb:使用总结

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月15日 09时25分35秒