背景
最近和朋友在建一个小站,主要展示图片的,对象存储服务囊中羞涩,就根据nginx自己弄一个图片存储服务,实现了动态压缩,裁剪以及缓存等功能。
ngx_http_image_filter_module
nginx 有很多现成好用的模块,ngx_http_image_filter_module就是专门用来处理图片的,如果一个网站需要缩略图或是指定尺寸的图片,image_filter_module会利用cpu动态对图片进行裁剪压缩,你不需要每次上传都先准备好 相应尺寸的缩略图存在硬盘本地。
主要是配置文件配置,直接贴出配置用法:
- 新建一个监听80的server,主要用于跳转https服务
- 在监听443 server的服务中,我们定义一个 location ,用于响应图片的url请求,注意它的root请求路径是/root/image,而我们的图片存在 /root/image/raw/2019-08-10/chenawen.jpg路径中
- https://img.***.cn/2019-08-10/chenawen.jpg!1080P 为其中一个图片的url请求路径,我们用~ /(.+)/([\w]+).(jpg|png|jpeg)!(1080P|720P|50P|720PI)$ 正则表达式去匹配这个请求
- proxy_cache 等相关配置先不看。
- valid_referers 是防盗链用的,通过header referer来判断图片请求是不是从 本站发出的,具体valid_referers用法参照官方文档 ngx_http_referer_module
- $4表示取 正则Groups 里的第四个参数,也就是取 (1080P|720P|480P) 里的其中一个值,根据我们举例的url,
$4
为 1080P ,$size
变量也为 1080P。 - 然后根据
$size
的值 设置图片的宽 变量$width
,为什么没有 图片的高呢,因为我们是做 图片的压缩,image_filter_module 会根据原照片的比例 和你给的$width
值,自动算出 高应该是多少,不需要你特意指定,如果你指定了,也会因为图片本身的比例问题,导致你设置的值不会起效果。 - 设置变量
$filename
, 文件名+后缀 - 随后检查下 有没有源文件,没有的话当然 直接返回 200 “图片不见了”
- 有源文件的话 就会用proxy_pass 反向代理到到image-resize服务
- image_filter服务就跟ngx_http_image_filter_module有关了,具体使用方法可以参照官方文档 ngx_http_image_filter_module
# img服务器代理(图片服务)
server {
listen 80;
server_name img.8888.cn;
rewrite ^(.*)$ https://$host$1 permanent;
location / {
}
}
server {
root /root/image;
listen 443 ssl http2;
server_name img.8888.cn;
ssl_certificate "/etc/nginx/1_img.8888.cn_bundle.crt";
ssl_certificate_key "/etc/nginx/2_img.8888.cn.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
#https://img.8888.cn/2019-08-10/chenawen.jpg!1080P 匹配方式
location ~* /(.+)\/([\w]+)\.(jpg|png|jpeg)\!(1080P|720P|50P|720PI)$ {
proxy_cache image_cache;
proxy_cache_valid 200 1m;
proxy_set_header Host $host;
valid_referers none blocked 8888.cn *.8888.cn *.localhost localhost *.baidu.com *.google.com;
if ($invalid_referer) {
return 403;
}
set $size $4;
if ($size = 1080P){
set $width 1920;
}
if ($size = 720P){
set $width 1280;
}
if ($size = 480P){
set $width 480;
}
# 2019-08-10
set $filepath $1;
# chenawen.jpg
set $filename "$2.$3";
#检查是不是有源文件
if (!-f $document_root/raw/$filepath/$filename) {
add_header Content-Type "text/html;charset=utf-8";
return 200 "图片不见了";
}
#没有本地缓存,则反向代理到image-resize
proxy_pass http://127.0.0.1:6669/image-resize/$filepath/$filename?width=$width;
}
location / {
add_header Content-Type "text/html;charset=utf-8";
return 200 "非法访问";
}
}
server {
listen 127.0.0.1:6669;
root /root/image;
#匹配满足 /image_resize的URI,生成压缩图
location /image-resize {
rewrite /(image-resize)/(.*) /raw/$2 break;
# 根据参数,resize 找到的图片
image_filter resize $arg_width -;
# image_filter crop $arg_width $arg_height;
image_filter_jpeg_quality 90;
image_filter_buffer 10m;
# 禁止外网的访问,只允许本地访问
allow 127.0.0.0/8;
deny all;
}
}
缓存我们的图片
根据上面的配置后,基本上一个图片服务器就搭好了,而且还可以自由裁剪压缩,对前端也非常方便,友好。 但是我们知道,每次处理图片都需要经过读取源文件并动用cpu压缩的过程,这个过程在并发量大的时候是非常 耗资源的。能不能通过缓存压缩的图片来减少cpu的压力? 答案是可以的,而且用这个模块可以临时缓存,不会长久的占用本地磁盘资源。
我们利用的工具就是 nginx 的另一个模块 ngx_http_proxy_module,它提供了proxy_cache来缓存请求资源,当然也可以缓存我们的图片了。
首先我们需要在 http 代码块上定义好 缓存的规则
- proxy_cache_path 定义在 http 上。
- /root/image/cache代表缓存的路径
- levels=1:2 除了/root/image/cache, proxy_cache还会创建若干层级文件夹来分别存放缓存文件,这里指定了2级
- keys_zone=image_cache:10m image_cache表示这次配置的name,10m表示可以缓存的key个数
- inactive 表示缓存过期的检查周期,我这里是1m(1分钟)检查一次
- max_size 就是缓存的最大大小了,至于超过了大小,nginx可能就会动用LRU等算法淘汰一些缓存了。
# 缓存配置
proxy_cache_path /root/image/cache levels=1:2 keys_zone=image_cache:10m inactive=1m max_size=1g;
做好了配置后,我们就可以在location中启用proxy_cache。这段 在上面直接跳过了,这里补上
- proxy_cache指定我们的缓存策略名字,也就是 http中 配置的image_cache了。
- proxy_cache_valid 表示对指定的 HTTP 状态进行缓存,并指定缓存时间,我这里对200进行缓存,并且缓存1分钟。 前面inactive也是缓存过期检查,我查了下资料,优先级从高到低一次为:inactive配置项、源服务器设置的Expires、源服务器设置的Max-Age、proxy_cache_valid配置项
proxy_cache image_cache;
proxy_cache_valid 200 1m;
测试
是骡子是马,要拿出来溜溜,经过配置后,我们通过 nginx 的access日志来判断 缓存有没有生效。
- 第一次访问能看到跳转 image-resize的服务,也就是说启用了ngx_http_image_filter_module模块,
- 而到了第二次访问,就没有了跳转image-resize的请求,而图片完美显示,说明缓存生效
- 这样就完成了 图片服务+缓存的服务。