nginx cache - 抵抗高并发的同时为你的网站加速

线上产品中有一台服务器的CPU波动比较明显,在用户爆发时可能会存在宕机的风险,登录服务器之后发现是MySQL进程的CPU占用在波动,是因为业务接口直接查询数据库,经过小组讨论后,决定通过短期缓存的形式避免每一个请求都命中数据库。

该服务器目前的内部架构如图:

为了对项目作最少的改动实现我们的需求,我们决定在nginx网关层启用nginx的代理缓存功能。

项目中使用的nginx是以docker形式运行的nginx-proxynginx默认是集成了proxy-cache模块的,我们只需配置一些参数即可:

①、既然要做缓存,那得有个地方存吧!在nginx.confhttp节点下配置一个缓存空间:proxy_cache_path 官方解释

...
http {
  default_type text/html;
  sendfile on;
  keepalive_timeout 65;
  
  proxy_cache_path /mycache/ levels=1:2 keys_zone=my_cache:10m max_size=500m inactive=60m use_temp_path=off;
  
  server {
    listen 80; 
    ...

简单解释一下:

  • /path/to/cache #本地路径,用来设置Nginx缓存资源的存放地址
  • levels #默认所有缓存文件都放在同一个/path/to/cache下,但是会影响缓存的性能,因此通常会在/path/to/cache下面建立子目录用来分别存放不同的文件。假设levels=1:2,Nginx为将要缓存的资源生成的key为f4cd0fbc769e94925ec5540b6a4136d0,那么key的最后一位0,以及倒数第2-3位6d作为两级的子目录,也就是该资源最终会被缓存到/path/to/cache/0/6d目录中
  • key_zone #简单的理解就是给这个缓存空间取个名字并指定一下能同时缓存多少个不同的请求。1m可以存储8000个,10m可以存储80000个
  • max_size #最大cache空间,如果不指定,会使用掉所有disk space,当达到配额后,会删除最少使用的cache文件
  • inactive #这是缓存文件的最大保留时间,缓存过期后文件不一定被删,但是在这个时间内没有访问,那就一定会被删,官方解释翻译:在inactive参数指定的时间内未访问的缓存数据将从缓存中删除,无论其新鲜度如何。默认情况下,inactive设置为10分钟。
  • use_temp_path #如果为off,则nginx会将缓存文件直接写入/path/to/cache文件夹,否则nginx先写入一个临时文件夹,再复制到/path/to/cache文件夹。官方建议为off,避免文件在不同文件系统中不必要的拷贝

②、有了缓存空间,那就该拿来用了。在你希望使用缓存的location节点下配置下面的参数:

proxy_buffering on; #注意,必须开启proxy_buffering才能够支持缓存,否则不会缓存。
proxy_cache my_cache; #指定使用上面配置的my_cache缓存空间

详细的官方配置参数:

proxy_cache
proxy_cache_background_update
proxy_cache_bypass
proxy_cache_convert_head
proxy_cache_key
proxy_cache_lock
proxy_cache_lock_age
proxy_cache_lock_timeout
proxy_cache_max_range_offset
proxy_cache_methods
proxy_cache_min_uses
proxy_cache_path
proxy_cache_purge
proxy_cache_revalidate
proxy_cache_use_stale
proxy_cache_valid

了解了这些基本的配置之后,我们可以轻松的在nginx-proxy上启用缓存:

编写配置缓存空间的配置文件cache.conf

proxy_cache_path /cache/ levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=12h use_temp_path=off;

编写使用缓存的配置文件vhost.d/xxx.domain.com_location

proxy_buffering on;
proxy_cache my_cache;
proxy_cache_valid 200 2s;
proxy_cache_methods GET;
add_header x-cache $upstream_cache_status;
# HIT / MISS / BYPASS / EXPIRED

接下来修改docker-compose.yml编排文件,将这两个文件夹挂载到容器内,nginx-proxy会在合适的位置自动引入这两个文件

gateway:
  image: daocloud.io/daocloud/nginx-proxy
  ports:
    - "8082:80"
  volumes:
    - /var/run/docker.sock:/tmp/docker.sock:ro
    - ./cache.conf:/etc/nginx/conf.d/cache.conf:ro
    - ./vhost.d:/etc/nginx/vhost.d:ro
  restart: always

然后重启一下网关就好了

可以通过查看响应头检查缓存的效果:

server: nginx/1.9.12
date: Sun, 15 Jul 2018 11:25:27 GMT
content-type: text/html; charset=utf-8
transfer-encoding: chunked
connection: keep-alive
cache-control: max-age=43200
expires: Sun, 15 Jul 2018 22:25:00 GMT
x-cache: HIT #首次访问时应该时MISS,紧接着再次访问则为HIT,就表示缓存开启成功。

其实在开缓存这个过程中,我折腾了很久,配来配去都没有效果,也查看了nginx-proxy的配置模板源码,依然总是MISS,Google到别人的答案也都没有效果,最后再仔细得阅读了nginx-proxy的模板文件,proxy_buffering参数引起了我的怀疑,搜索并了解之后,问题得到了解决,原因是nginx-proxy默认是关闭proxy_buffering的,而实现proxy_cache必须要开启proxy_buffering。于是我准备到GitHub上给作者一个issue,搜索后发现早有人提出这个问题了https://github.com/jwilder/nginx-proxy/issues/800

综上所述:遇到问题有两个终极解决方案:

1、阅读源码,这也是使用开源项目的好处。

2、查看项目的Issues列表以及官方论坛,你踩的坑也许别人早跳出来过了。

增加缓存机制之后,接口响应速度快至4ms 。这就是缓存的魅力所在了。

参考连接:https://blog.csdn.net/wangjianno2/article/details/75201020/

评论