最近参与公司一项目,当中需使用图片与音频的存储方案,经过多方面考虑,采用了Nginx的ngx_upload_module作为上传前端,python web.py+gevent作为后端文件处理及生成缩略图方式,配合使用Varnish作为http缓存。整体架构与性能上应该比较理想。
前期由于考虑了分布式存储,大量地实验与尝试了fastDFS,感觉的确是小文件存储方案里面比较优秀的,但是由于对fastDFS的不熟悉与稳定性的考虑,暂时放下。
实现时,参考了大量py-graphic-0.1.1的思路,感谢作者。
https://code.google.com/p/py-graphic/
1、实现原理
由Nginx+nginx_upload_module实现接收http Post请求,并将用户文件保存到nginx.conf指定的位置,这些文件信息从原始请求体中分离并根据nginx.conf中的配置重新组装好上传参数,交由upload_pass指定的段处理,从而允许处理任意上传文件。每个上传文件中的file字段值被一系列的upload_set_form_field指令值替换。每个上传文件的内容可以从$upload_tmp_path变量读取,或者可以将文件转移到目的目录下。上传的文件移除可以通过upload_cleanup指令控制。如果请求的方法不是POST,模块将返回405错误(405 Not Allowed),该错误提示可以通过error_page指令处理。
upload_pass指定为proxy_pass地址,将上传结果转由gevent+web.py进行处理。通过web.input()获取所有参数,包括文件实际路径与大小,Md5等字段。如果是图片格式,则通过pgmagick组件对图片进能剪栽切割生成缩略图。然后将原图与缩略图保存到web目录下,最后对客户端返回JSON格式的Varnish缓存地址。
2、所需用到的依赖项
以CentOS 最小化安装为例。
yum -y install gcc gcc-c++ autoconf make python python-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-devel curl curl-devel e2fsprogs e2fsprogs-devel krb5 krb5-devel libidn libidn-devel openssl openssl-devel openldap openldap-devel nss_ldap openldap-clients openldap-servers
单独编译安装:
boost_1_50_0 setuptools-0.6c11-py2.6.egg gevent-1.0rc2 GraphicsMagick-1.3.16 pcre-8.10 pgmagick-0.5.4
安装Nginx + ngx_upload_module 2.2。
安装过程可以参考http://blog.s135.com/nginx_php_v6/
3、配置nginx.conf
具体ngx_upload_module配置参数,请参考官网。
user www www; worker_processes 4; error_log /*自定义路径*/nginx_error.log crit; pid /usr/local/nginx/nginx.pid; worker_rlimit_nofile 65535; events { use epoll; worker_connections 65535; } http { include mime.types; default_type application/octet-stream; server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 20m; sendfile on; tcp_nopush on; keepalive_timeout 60; tcp_nodelay on; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; server { listen 9000; server_name localhost; location / { index index.html index.htm; root /自定义路径; } location /PicUpload { upload_pass /PicProccess; upload_store /*自定义路径*/ 1; upload_store_access user:r; upload_set_form_field $upload_field_name.name "$upload_file_name"; upload_set_form_field $upload_field_name.content_type "$upload_content_type"; upload_set_form_field $upload_field_name.path "$upload_tmp_path"; upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5"; upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size"; upload_pass_form_field "^uid$|^thumb$"; #指定用户ID与缩略图尺寸,例如100x100 upload_cleanup 400 404 499 500-505; #遇到这些码,就清除上传内容。 } location /VoiceUpload { upload_pass /VoiceProccess; upload_store /*自定义路径*/ 1; upload_store_access user:r; upload_set_form_field $upload_field_name.name "$upload_file_name"; upload_set_form_field $upload_field_name.content_type "$upload_content_type"; upload_set_form_field $upload_field_name.path "$upload_tmp_path"; upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5"; upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size"; upload_pass_form_field "^uid$"; #post带uid域 upload_cleanup 400 404 499 500-505; } location /PicProccess { proxy_pass http://127.0.0.1:9020/PicUpload; } location /VoiceProccess { proxy_pass http://127.0.0.1:9020/VoiceUpload; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /自定义路径; } log_format access '$remote_addr - $remote_user[$time_local] "$request"' '$status $body_bytes_sent "$http_referer"' '$http_user_agent" $http_x_forwarded_for'; access_log /日志路径/nginx_access.log access; } }
4、 创建upload_store 存储位置。
由于ngx_upload_module是散列存储,所以子目录需要包含 0 1 2 3 4 5 6 7 8 9 十个目录。
具体可参考http://www.grid.net.ru/nginx/upload.en.html
5、开源Github地址
https://github.com/vovolie/nginx_upload
十分简单的代码,可随意修改。
目录结构
bin : 包括Daemon守护进程,wsgiServer.py主程序。
conf:日志配置与程序配置文件。
log:日志存放位置。
test:post测试的小程序。
6、安装varnish,如果是同一台服务器,需指定不同的端口,在conf配置文件中进行修改返回的地址。