Nginx Proxy_cache 实践 (with Docker, Ngx-cache-purge, Etag)

最近在用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
  }
}

相关参考

都在文中啦

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281