最近參與公司一項目,當中需使用圖片與音頻的存儲方案,經過多方面考慮,采用了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配置文件中進行修改返回的地址。
