美文网首页
Nginx Proxy_cache 实践 (with Docke

Nginx Proxy_cache 实践 (with Docke

作者: bysir | 来源:发表于2019-03-23 18:54 被阅读0次

    最近在用Nuxt.js做一个SSR项目, 第一次做当然是踩坑不少, 其中一个坑就是Nuxt文档所提到的: 将nginx与生成的页面和缓存代理一起使用, 文档就一页, 但实际可不只这么简单.

    至于为什么要选用Nuxt做项目, 而不是传统的字符串模板引擎或者是前端渲染呢? 可以看我另一篇文章: 使用SSR优化Vue的首屏加载速度与SEO (In writing :D)

    下面就将阐述整个项目使用到的知识点

    Nginx

    nginx一般用于在项目中反向代理, 实现负载/静态资源服务器等功能, 可以说项目必备了.

    在这里, 我们将使用nginx反向代理node服务(nuxt的ssr服务), 并实现代理缓存(proxy_cache).

    为什么要缓存呢? 实在是SSR效率太低, 访问一个稍微复杂一点的页面需要300ms的时间才能渲染并返回完毕, 有点隐隐担忧.

    系统优化的最终方案是不调用系统, 缓存就能达到这个目的.

    Nginx in docker

    如果你受够了在机器上敲上百行命令行去初始化你的项目环境(安装依赖等), 那么docker的好就不言而喻了.

    docker镜像中包含了整个项目甚至是操作系统, 所以通常一个镜像比单个软件大很多, 为了不必要的磁盘占用, alpine就应运而生了.

    Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.

    基于alpine构建的nginx镜像仅仅只有9M, 而基于debian构建的却有55M,你可以到这个页面去获取镜像并查看详情: nginx Tags - Docker Hub.

    当然alpine也不是没有缺点, 由于精简到极致, 所以很多命令都不能使用, 这可能会造成不太容易去编写命令(如Dockerfile).

    Proxy-cache-purge

    在nginx中使用proxy-cache指令就能开启代理缓存, 但如果想刷新缓存怎么办? 在官方文档中提到了这个, 但是NGINX Plus才支持....

    NGINX Plus supports selective purging of cached files. This is useful if a file has been updated on the origin server but is still valid in the NGINX Plus cache (the Cache-Control:max-age is still valid and the timeout set by the inactive parameter to the proxy_cache_path directive has not expired). With the cache‑purge feature of NGINX Plus, this file can easily be deleted. For more details, see Purging Content from the Cache.

    仅仅需要这么几行nginx配置

    proxy_cache_path /tmp/cache keys_zone=mycache:10m levels=1:2 inactive=60s;
    
    map $request_method $purge_method {
        PURGE 1;
        default 0;
    }
    
    server {
        listen 80;
        server_name www.example.com;
    
        location / {
            proxy_pass http://localhost:8002;
            proxy_cache mycache;
    
            proxy_cache_purge $purge_method;
        }
    }
    

    好像挺简单, 但我们平民只有另辟蹊径了.

    百度/谷歌"proxy_cache_purge", 再在 https://hub.docker.com 搜一下"nginx cache pruge", 果然有友军做了这个: procraft/nginx-purge.

    不过他不是基于alpine做的, 那么现在就只有自己动手了, 其实有了参考就都好办.

    更多的说明就不写了.

    如果你想继续研究这份Dockerfile是怎么写的, 可以去我的github仓库查看 nginx-docker.

    如果你想直接用这个镜像, 可以去我的dockerhub查看 bysir/nginx.


    安装好了这个扩展Module之后就可以编写配置文件了.

    由于我们并不是使用的Nginx Plus, 所以上面的配置是不生效的(这个坑我踩过).

    那么该怎么写配置呢?

    在我们安装的Module nginx-modules/ngx_cache_purge 仓库中有说明.

    一般来说这样一个配置就够用了.

    http {
        proxy_cache_path  /tmp/cache  keys_zone=tmpcache:10m;
    
        server {
            location / {
                proxy_pass         http://127.0.0.1:8000;
                proxy_cache        tmpcache;
                proxy_cache_key    $uri$is_args$args;
                proxy_cache_purge  PURGE from 127.0.0.1;
            }
        }
    }
    

    如果不想限制访问ip, 则直接这样写也可以: proxy_cache_purge PURGE;

    如果想清除缓存, 仅仅需要这样

    # curl -X PURGE "http://yourdomain.com/*"
    

    * 代表清除所有缓存, 详情查阅刚刚所提文档中的partial-keys.

    自定义 proxy_cache_key

    有时候$uri$is_args$args这样的key不能满足需求, 比如后端对于PC和Mobile有两套界面, 是通过UA判断的, 这时候如果在Nginx缓存就会出现问题.

    这时候就需要自定义缓存key.
    可以这样

    location / {
                set $ua "pc"
                proxy_pass         http://127.0.0.1:8000;
                proxy_cache        tmpcache;
                proxy_cache_key    $uri$is_args$args$ua;
                proxy_cache_purge  PURGE from 127.0.0.1;
    }
    

    With Nuxt.js

    进入实战, 现在将编写配置文件代理Nuxt项目.

    可以参考Nuxt所写的文档 nginx-proxy, 建议查看英文文档, 中文翻译有点不准确.

    在各种试错下, 终于写出一个可以正常使用的的配置, 如下

    ps: 我会尽量为每一段代码写上注释, 但nginx的各个指令真的难理解, 脑子不够用, 等以后有缘了再逐个弄清吧.

    proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=cache:10m max_size=10g;
    
    server {
        listen       80;
    
        location / {
            add_header X-Cache-Status $upstream_cache_status; # 添加上缓存状态, 有Hit和Miss等. 
            proxy_ignore_headers Cache-Control; # (nuxt推荐写法), 我猜是忽略浏览器的强制刷新发送的缓存控制头, 让强制刷新也不会击穿缓存.
            proxy_cache_valid 500 1m; # 如果服务端返回500则只缓存1分钟
            proxy_cache_valid any 30m; # 对于其他相应, 缓存30分钟, 这些数值根据你的业务调整
            proxy_http_version 1.1;
            proxy_cache cache; # keys_zone
            proxy_cache_bypass $arg_nocache; # 使用?nocache=1这样的请求参数跳过使用缓存
            proxy_cache_key $host$uri$is_args$args; # 缓存key, 如果是当前nginx在服务多个域名, 则需要添加上$host, 否则可以不用$host.
            proxy_cache_purge PURGE; # usage: curl -X PURGE "http://youdomain.com/*"
            proxy_redirect off;
            proxy_cache_background_update off; # 缓存预热, 根据你的需求而定. 
            proxy_cache_lock on; # 如果当个请求在请求同一个key并且没有缓存, 就会将后续的请求block, 防止缓存击穿.
            proxy_cache_revalidate on; # 是否让缓存重新生效. 如果开启 当缓存过期, 但是Etag相等的情况下, 会将这个缓存重新生效.
        }
    }
    

    对于上面的配置项, 都可以去nginx-caching-guidengx-http_proxy_module查阅.

    Etag

    Etag是用来做缓存验证的, 浏览器在请求资源的时候如果带上了Etag, 那么服务端端就可以根据Etag来判断是否需要返回新的内容给浏览器, 如果Etag相等, 那么服务端就会返回304告知浏览器当前的资源是最新的 可以拿来使用, 而不是返回整个资源给浏览器, 大大节约了流量传输的时间.

    我当然是期望nginx的proxy_cache是支持Etag的, 但是当我配置好以后, 却发现一直是200.

    百度无果(习以为常), google上找到了一篇文章: nginx-proxy-cache-and-etag, 但也没有很好得解决办法.

    最后只得让服务端(也就是Nuxt)去计算Etag, 经过试验Nginx也能缓存命中, 就这样吧.

    如果你想做试验, 可以在nuxt.config.js里配置关闭Etag, 这在nuxt中是默认开启的.

    module.exports = {
      render: {
        etag: false
      }
    }
    

    相关参考

    都在文中啦

    相关文章

      网友评论

          本文标题:Nginx Proxy_cache 实践 (with Docke

          本文链接:https://www.haomeiwen.com/subject/zfusvqtx.html