HTTP Cache 总结及Nginx Cache配置

作者:链上研发
时间:2016-09-21

简介

先说一下我对缓存的理解:缓存可以让用户更加接近数据

HTTP缓存指我们用浏览器访问网站时,根据服务器返回的HTTP缓存响应头设置,缓存相应的数据,下次访问就可以直接使用,或者去服务器验证数据是否过期。这样可以大大减轻宽带压力,加快网页加载速度。

相关头部

当我们首次访问http://sage.moe/content/images/2016/07/masuzu-desktopsky-22521-jpg-900.jpg 时,得到如下响应头:

服务器返回如下几个缓存控制头部:

  1. Last-Modified:表示文档的最后修改时间,当去服务器验证时会拿这个时间去;
  2. Expires:http/1.0规范定义,表示文档在浏览器中的过期时间,当缓存的内容超过这个时间则需要重新去服务器获取最新的内容;
  3. Cache-Control:http/1.1规范定义,表示浏览器缓存控制,max-age=3153600表示文档可以在浏览器中缓存一年。
  4. ETag:发送到服务端进行内容变更验证的,而Catch-Control是用于控制缓存时间的(浏览器、代理层等)。此处我们使用了弱实体W\”6124c”,弱实体(”6124c”)只要内容语义没变即可,比如内容的gzip版和非gzip版可以使用弱实体验证;而强实体指字节必须完全一致(gzip和非gzip情况是不一样的),因此建议首先选择使用弱实体。nginx在生成etag时使用的算法是Last-Modified + Content-Length计算的。

根据规范定义Cache-Control优先级高于Expires,实际使用时可以两个都用,或仅使用Cache-Control就可以了。一般情况下Expires=当前系统时间(Date) + 缓存时间(Cache-Control: max-age)。

当我们再次访问http://sage.moe/content/images/2016/07/masuzu-desktopsky-22521-jpg-900.jpg 时,发现浏览器请求头部有变化:

1. Modified-Since请求头,其值是上次请求响应中的Last-Modified,即浏览器会拿这个时间去服务端验证内容是否发生了变更。
2. If-None-Match请求头,其值是上次请求响应中的ETag,即浏览器会拿这个时间去服务端验证内容是否发生了变更。

Last-Modified与ETag同时使用时,浏览器在验证时会同时发送If-Modified-Since和If-None-Match,按照http/1.1规范,如果同时使用If-Modified-Since和If-None-Match则服务端必须两个都验证通过后才能返回304;且nginx就是这样做的。因此实际使用时应该根据实际情况选择。

当我们按下cmd-F5强制刷新后:

浏览器在请求时不会带上If-Modified-Since,并带上Cache-Control:no-cache和Pragma:no-cache,这是为了告诉服务端说我请给我一份最新的内容。

相关状态码

  1. 304 Not Modified

304表示服务器返回文档没有过期

  1. 200 (from cache)

    200 (from cache) 表示浏览器直接使用缓存数据

总结

  1. Last-Modified/Modified-Since 用于验证文档内容是否变更
  2. max-age/Expires 定义文档缓存时间,如在有效期内会返回 200(from cache)

Nginx 作为静态文件服务器

主要配置:

  1. expires:控制Expires响应头
  2. etag (off):控制是否返回Etag响应头
  3. if-modified-since (before/exact): exact表示精确匹配,before表示文件修改时间早于浏览器If-Modified-Since时间,就返回304

静态文件配置:

Nginx作为反向代理

nginx作为反向代理,所有请求都会先请求至nginx,再由nginx转发

如果使用Expires可以修改返回浏览器Expires头部信息,但是nginx只负责转发请求和负载均衡,没有为后端服务器分担压力。

这是我们需要nginx在本地对访问内容进行缓存,如果文件还在缓存期内,由nginx返回304响应,否则再转发至后台,并且nginx再次更新自己的本地缓存。

nginx本地缓存http配置:

主要介绍下proxy_cache_path配置

  1. levels:表示创建两级目录结构,比如/export/cache/proxy_cache//*/,将所有文件放在一级目录结构中如果文件量很大会导致访问文件效率低。
  2. keys_zone:设置存储所有缓存key和相关信息的共享内存区,1M大约能存储8000个key。
  3. inactive:inactive指定被缓存的内容多久不被访问将从缓存中移除,以保证内容的新鲜;默认10分钟。
  4. max_size :最大缓存阀值,“cachemanager”进程会监控最大缓存大小,当缓存达到该阀值,该进程将从缓存中移除最近最少使用的内容。
  5. use_temp_path:如果为on,则内容首先被写入临时文件(proxy_temp_path ),然后重命名到proxy_cache_path指定的目录;如果设置为off,则内容直接被写入到proxy_cache_path指定的目录。

nginx本地缓存location配置:

1. proxy_cache :指定使用哪个共享内存区域存储缓存键和相关信息;
2. proxy_cache_key :设置缓存使用的key,默认为访问的完整URL,根据实际情况设置缓存key;
3. proxy_cache_valid :为不同的响应状态码设置缓存时间;如果是proxy_cache_valid 5s 则200、301、302响应将被缓存;

  1. proxy_cache_valid
    proxy_cache_valid不是唯一设置缓存时间的,还可以通过如下方式(优先级从上到下):
    1. 以秒为单位的“X-Accel-Expires”响应头来设置响应缓存时间
    2. 如果没有“X-Accel-Expires”,可以根据“Cache-Control”、“Expires”来设置响应缓存时间
    3. 否则使用proxy_cache_valid设置的缓存时间

如果响应头包含Cache-Control:private/no-cache/no-store、Set-Cookie或者只有一个Vary响应头且其值为*,则响应内容将不会被缓存。可以使用proxy_ignore_headers来忽略这些响应头。

  1. proxy_cache_lock
    当多个客户端同时请求同一份内容时,如果开启proxy_cache_lock(默认off)则只有一个请求被发送至后端;其他请求将等待该内容返回;当第一个请求返回时,其他请求将从缓存中获取内容返回;当第一个请求超过了proxy_cache_lock_timeout超时时间(默认5s),则其他请求将同时请求到后端来获取响应,且响应不会被缓存;启用proxy_cache_lock可以应对雪崩效应。

清理缓存

有时候缓存的内容是错误的,需要手工清理,可以使用ngx_cache_purge模块进行清理缓存,如:

这样只允许本地可以访问,另外也可以配置password访问

总结

  1. 缓存虽然不是什么很高深的的技术,但是缓存是抗流量冲击的银弹,起到的作用举足轻重
  2. 静态文件是可以缓存很久的
  3. 缓存是可以预热的
  4. 如果业务场景允许,可以做开关,在遭受到流量冲击的时候全部走缓存,不回流到后端服务器
  5. 只缓存200的响应, 后端响应错误不要返回200
  6. 缓存的key设置需要合理,可以做个工具,快速删除不正确的缓存

2017-03-23更新

今天线上缓存出了问题,发现响应头部竟然没有Cache-Control头部。关于没有Cache-Control头部的行为HTTP协议没有规定,只有建议(有可能我没看到)。找到了火狐浏览器的实现方式如下:

The freshness lifetime is calculated based on several headers. If a “Cache-control: max-age=N” header is specified, then the freshness lifetime is equal to N. If this header is not present, which is very often the case, it is checked if an Expires header is present. If an Expires header exists, then its value minus the value of the Date header determines the freshness lifetime. Finally, if neither header is present, look for a Last-Modified header. If this header is present, then the cache’s freshness lifetime is equal to the value of the Date header minus the value of the Last-modified header divided by 10.

简单来说没有Cache-Control头部,Cache-Control 默认为private, max-age=Date header 的值减去Last-modified header 值的10%

你可能感兴趣的:(架构)