写在前面的话
事情的起因是,目前的工作环境没有图床,so embarrassing…于是决定自己写一个图床吧,花了一点时间,代码写完了,php+swoole+mysql。问题来了,每次访问图片都需要一次数据库查询,这必然导致数据库鸭梨山大。解决的思路就是加缓存,在CDN之前,想在NGINX层加一下缓存。
因为后端是php+swoole,NGINX的配置大概如下
server {
listen 1706;
location / {
proxy_pass http://127.0.0.1:9606;
}
}
通常情况下,对图片类缓存配置大概是
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 3d;
}
此时访问后缀是jpg等的文件后,浏览器会做缓存,直到过期或者Disable Cache
那么,proxy_pass下,如何对请求做缓存呢?
p.s. 我的图床git地址 欢迎围观指正
困惑
梯子不好使,就懒得用谷歌了,在度娘的帮助下,也是找到了若干博文,标题也是相当吸睛,感觉马上就可以解决我的问题了,但是尝试无果,一次次失望,忍不住来自灵魂深处的发问:为什么我不行?
以为看个博文就可以解决,偷懒不想看NGINX的document,但是最后还是不得不去翻document,还浪费了大量时间,偷鸡不成蚀把米。学习一项东西最好的就是它的官方文档。
当然,本文也不一定能解决你的问题,望仅此作为一个解决问题的思路。
破局
翻过的博文不再赘述,当问题解决的一刹那,再也不想去看为什么按照之前的博文配置就是不行了,只要记住正确的方式即可,那些解决不了问题的方法,还管他作甚…
话不多说,切入正题。看那些博文也不是一无所获的,起码知道了有个proxy_cache选项配置,是可以解决这个问题的。
- 打开NGINX的官网
- 打开document
- 既然是proxy_cache这个选项,那么先搜索cache试试,发现不行,只有一个ngx_http_memcached_module,显然不是想要的
- 再搜索proxy,一共四个,挨着点进去,直到ngx_http_proxy_module,嗯,这就是要找的。当时的心情不亚于哥伦布发现新大陆吧,可是给劳资找到了
- proxy_cache
- proxy_cache_background_update
- proxy_cache_bypass
- …
proxy_buffering
Syntax: proxy_buffering on | off;
Default: proxy_buffering on;
Context: http, server, location
原文中提到Enables or disables buffering of responses from the proxied server.
也就是说要想NGINX对proxy的response进行缓存,这个选项必须是on的状态。关于缓存的设置详见proxy_buffer_size和proxy_buffers这两个配置项。如果response过大,还可以通过proxy_temp_path配置一个临时文件目录保存那些临时文件,而临时文件的配置选项有两个:proxy_max_temp_file_size和proxy_temp_file_write_size。
proxy_temp_path
Syntax: proxy_temp_path path [level1 [level2 [level3]]];
Default: proxy_temp_path proxy_temp;
Context: http, server, location
设置临时目录的路径,这里最多可以设置3级子目录,在请求量很大的情况下,可以有效的防止单个文件夹下文件过多的情况。
至于要不要把临时文件缓存到临时目录,则是在proxy_cache_path这个选项中配置的。
proxy_cache_path
Syntax: proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
Default: —
Context: http
这个配置项很重要,项目也很多,需要好好看看。至于上面提到的要不要用temp目录,则是通过use_temp_path=on|off来控制的。keys_zone则是指定了用于缓存的zone和大小,这个zone会在proxy_cache里用到。
另外还可以通过proxy_cache_key 设置缓存的key。
proxy_cache
Syntax: proxy_cache zone | off;
Default: proxy_cache off;
Context: http, server, location
zone就是上面proxy_cache_path里定义的keys_zone的name
写到这里,对于基本需求,大部分的配置就这些了,看一下现在的配置
proxy_cache_path /dev/shm/proxy_cache levels=1:2 keys_zone=one:20m inactive=5m max_size=1024m use_temp_path=on;
server {
listen 1706;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
proxy_temp_path /dev/shm/proxy_tmp 1 2;
proxy_max_temp_file_size 1024m;
proxy_temp_file_write_size 16k;
proxy_cache_key $scheme$proxy_host$request_uri;
location / {
proxy_cache one;
proxy_pass http://127.0.0.1:9606;
proxy_set_header request_id $request_id;
}
}
为什么说是大部分呢,因为如果只是这些配置,还不能起到想要的结果,因为默认状态是200的response是不会缓存的!
因为还没有看完ngx_http_proxy_module这块的document,这个结论暂时没有从document里找到出处。发现这个问题是,在以上配置尝试无果后,配置了一个proxy到https://www.baidu.com的location,结果给缓存了,这个code是302。也算阴差阳错了。
要想缓存code是200的response,必须设置proxy_cache_valid。
proxy_cache_valid
Syntax: proxy_cache_valid [code ...] time;
Default: —
Context: http, server, location
如果只设置时间不设置code,则只有code是200, 301以及302的response会缓存。
再加上这个配置项,就大功告成了!
这时候还有一个问题,如果数据更新了,但是缓存还没过期,想要最新的数据怎么办?
这时候需要两个配置项:proxy_cache_bypass和proxy_no_cache
proxy_cache_bypass
Syntax: proxy_cache_bypass string ...;
Default: —
Context: http, server, location
Defines conditions under which the response will not be taken from a cache.
proxy_no_cache
Syntax: proxy_no_cache string ...;
Default: —
Context: http, server, location
Defines conditions under which the response will not be saved to a cache
嗯…暂时还不是很清楚为什么要设置两个功能一样的配置项。实验的结果是,如果只配置了proxy_no_cache,最终并没有跳过缓存,而只配置proxy_cache_bypass是会跳过缓存的,当然两者都配置也是会跳过缓存的。这里要等待后续更深入的研究了
。
至此,需要的配置算是OK了,最终的配置项
proxy_cache_path /dev/shm/proxy_cache levels=1:2 keys_zone=one:20m inactive=5m max_size=1024m use_temp_path=on;
server {
listen 1706;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
proxy_temp_path /dev/shm/proxy_tmp 1 2;
proxy_max_temp_file_size 1024m;
proxy_temp_file_write_size 16k;
proxy_cache_key $scheme$proxy_host$request_uri;
proxy_cache_valid 5m;
proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment $http_pragma;
location / {
proxy_cache one;
proxy_pass http://127.0.0.1:9606;
proxy_set_header request_id $request_id;
}
}
尾声
还有一些遗留的困惑需要解决