背景

最近和朋友在建一个小站,主要展示图片的,对象存储服务囊中羞涩,就根据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模块,
  • nginx image filter nginx image filter 缓存_缓存

  • 而到了第二次访问,就没有了跳转image-resize的请求,而图片完美显示,说明缓存生效
  • nginx image filter nginx image filter 缓存_image_02

  • 这样就完成了 图片服务+缓存的服务。