nginx-image模块和fastdfs module的协作

FastDFS组合nginx的http_image_filter_module建立的图片服务器,实现动态缩略图

用fastdfs存储图片,然后用nginx的图片处理模块http_image_filter_module处理图片,根据输入的地址中的图片大小动态生成缩略图

原图http://192.168.8.127:801/group1/ ... WAAL8RoEHXq8410.jpg

动态生成的缩略图地址

http://192.168.8.127:801/group1/ ... HXq8410,222x222.jpg

http://192.168.8.127:801/group1/ ... HXq8410,555x555.jpg

通过在图片后面加入,axb

就能生成a,b为参数的缩略图

方法:

1、首先安装好fastdfs,并可以保证上传图片和访问图片正常(不会了,参考群里“Richard老兄写的FASTDFS 安装配置指南”这个文章写的很简单明了)

2、然后下载FastDFS的fastdfs-nginx-module模块

3、编译安装nginx,编译nginx的时候,注意带上这个2个模块,一个是nginx调用fastdfs的模块,一个是nginx的图片处理模块

1
--add-module=/software/fastdfs-nginx-module/src       --with-http_image_filter_module  

4、安装好nginx后,启动nginx和fastdfs,修改nginx的配置文件nginx.conf,修改/etc/fdfs/mod_fastdfs.conf

mod_fastdfs.conf需要修改的如下:

1
2
3
4
5
6
7
8
9
base_path=/fastdfs
tracker_server=172.16.3.15:22122
tracker_server=172.16.3.16:22122
group_name=group1
url_have_group_name = true
store_path_count=1
store_path0=/fastdfs
response_mode=proxy
鱼大说responese_mode如果为redirect,那么手机看图片会有问题

nginx.conf需要修改的如下:

在server段插入如下配置段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
location ~ /group[0-9]/M[0-1][0-9]/.*,([0-9]+)x([0-9]+)\.(jpg|png|gif)$ {

alias /fastdfs/data;
ngx_fastdfs_module;

set $img_width2 $1;
set $img_height2 $2;
rewrite ^(.*),[0-9]+x[0-9]+\.(jpg|png|gif)$ $1.$2 break;
image_filter resize $img_width2 $img_height2;
}

location ~ /group[0-9]/M[0-1][0-9]/(.*){
alias /fastdfs/data;
ngx_fastdfs_module;
}

配置就是这么多

... HXq8410.jpg是group1的图片

... HXq8411.jpg是group2的图片

http://192.168.8.127:801/group1/ ... HXq8410.jpg

http://192.168.8.127:801/group2/ ... HXq8411.jpg

http://192.168.8.127:801/group1/ ... HXq8410,222x222.jpg

http://192.168.8.127:801/group2/ ... HXq8411,222x222.jpg

都应该能访问的到

然而http://192.168.8.127:801/group2/ ... HXq8411,222x222.jpg,这个跨组调用,经常会返回HTTP/1.1 415 Unsupported Media Type

经过初步的诊断,由于nginx的流式处理,fdfs_module有时候在尚未完全下载到图片的时候,image module就开始运作了,这时image module的缓冲区是0字节数据,因此直接对图片格式判断不支持,从而导致出错。对image module简单的修改,使得fdfs_module和image_module可以协作不报错。

修改了image模块的一部分来使得两模块协作正常


在贴代码之前,首先稍微对Nginx的I/O机制做一定的介绍,Nginx允许handler一次产生一组输出,可以产生多次,Nginx将输出组织成一个单链表结构,链表中的每个节点是一个chain_t,定义在core/ngx_buf.h:

1
2
3
4
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};

其中ngx_chain_t是ngx_chain_s的别名,buf为某个数据缓冲区的指针,next指向下一个链表节点,可以看到这是一个非常简单的链表。ngx_buf_t的定义比较长而且很复杂,这里就不贴出来了,请自行参考core/ngx_buf.h。ngx_but_t中比较重要的是pos和last,分别表示要缓冲区数据在内存中的起始地址和结尾地址,这里我们将配置中字符串传进去,last_buf是一个位域,设为1表示此缓冲区是链表中最后一个元素,为0表示后面还有元素。因为我们只有一组数据,所以缓冲区链表中只有一个节点,如果需要输入多组数据可将各组数据放入不同缓冲区后插入到链表。下图展示了Nginx缓冲链表的结构:

nginx io介绍图

缓冲数据准备好后,用ngx_http_output_filter就可以输出了(会送到filter进行各种过滤处理)。ngx_http_output_filter的第一个参数为ngx_http_request_t结构,第二个为输出链表的起始地址&out。ngx_http_out_put_filter会遍历链表,输出所有数据。


下面是修改的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (ctx->type == NGX_HTTP_IMAGE_NONE) {

if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
out.buf = ngx_http_image_json(r, NULL);

if (out.buf) {
out.next = NULL;
ctx->phase = NGX_HTTP_IMAGE_DONE;

return ngx_http_image_send(r, ctx, &out);
}
}

if(in->buf->last == in->buf->pos)
return NGX_OK;
return ngx_http_filter_finalize_request(r,
&ngx_http_image_filter_module,
NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
}

插入的代码只有一句

1
2
if(in->buf->last == in->buf->pos)
return NGX_OK;

如果last和pos地址相同,说明图片尚未完整接收到,那么返回NGX_OK来跳过下面的步骤,等待下次流式处理。

用http_load对缩略图模块进行压测,全部是200的返回值,TPS并不高,瓶颈在nginx服务器的CPU上

如果比较懒,可以直接用https://github.com/tedcy/fastdfs_build这个我的开源fastdfs部署脚本进行部署

解压后./build -n tracker_ip1,tracker_ip2 -g group_name即可

如./build -n 192.168.1.1,192.168.1.2 -g 1

参考资料:

解剖Nginx·模块开发篇(1)跑起你的 Hello World 模块!

Nginx模块开发入门