首页 > 学院 > 开发设计 > 正文

web 应用中常用的各种 cache详解

2019-10-26 19:23:16
字体:
来源:转载
供稿:网友

本文以Nginx,Rails,Mysql,Redis作为例子,换成其他web服务器,语言,数据库,缓存服务都是类似的。
以下是3层的示意图,方便后续引用:

1. 客户端缓存

一个客户端经常会访问同一个资源,比如用浏览器访问网站首页或查看同一篇文章,或用app访问同一个api,如果该资源和他之前访问过的没有任何改变,就可以利用http规范中的304 Not Modified 响应头(http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5),直接用客户端的缓存,而无需在服务器端再生成一次内容。
在Rails里面内置了fresh_when这个方法,一行代码就可以完成:

class ArticlesController def show  @article = Article.find(params[:id])  fresh_when :last_modified => @article.updated_at.utc, :etag => @article endend

下次用户再访问的时候,会对比request header里面的If-Modified-Since和If-None-Match,如果相符合,就直接返回304,而不再生成response body。

但是这样会遇到一个问题,假设我们的网站导航有用户信息,一个用户在未登陆专题访问了一下,然后登陆以后再访问,会发现页面上显示的还是未登陆状态。或者在app访问一篇文章,做了一下收藏,下次再进入这篇文章,还是显示未收藏状态。解决这个问题的方法很简单,将用户相关的变量也加入到etag的计算里面:

  fresh_when :etag => [@article.cache_key, current_user.id]  fresh_when :etag => [@article.cache_key, current_user_favorited]

另外提一个坑,如果nginx开启了gzip,对rails执行的结果进行压缩,会将rails输出的etag header干掉,nginx的开发人员说根据rfc规范,对proxy_pass方式处理必须这样(因为内容改变了),但是我个人认为没这个必要,于是用了粗暴的方法,直接将src/http/modules/ngx_http_gzip_filter_module.c这个文件里面的这行代码注释掉,然后重新编译nginx:

  //ngx_http_clear_etag(r); 

或者你可以选择不改变nginx源代码,将gzip off掉,将压缩用Rack中间件来处理:

  config.middleware.use Rack::Deflater

除了在controller里面指定fresh_when以外,rails框架默认使用Rack::ETag middleware,它会自动给无etag的response加上etag,但是和fresh_when相比,自动etag能够节省的只是客户端时间,服务器端还是一样会执行所有的代码,用curl来对比一下。
Rack::ETag自动加入etag:

curl -v http://localhost:3000/articles/1< Etag: "bf328447bcb2b8706193a50962035619"< X-Runtime: 0.286958curl -v http://localhost:3000/articles/1 --header 'If-None-Match: "bf328447bcb2b8706193a50962035619"'< X-Runtime: 0.293798用fresh_when: curl -v http://localhost:3000/articles/1 --header 'If-None-Match: "bf328447bcb2b8706193a50962035619"'< X-Runtime: 0.033884            
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表