转载请备注来源: 《Nginx缓存机制详解》 | shuwoom.com

缓存示意图

Nginx缓存作为性能优化的一个重要手段,可以极大减轻后端服务器的负载。在这篇文章中,我们将介绍nginx缓存配置的相关指令以及http缓存机制,以及nginx缓存实践案例分析。

缓存示例

当我们代开某个网站,如baidu.com,我们可以看到size这一列有一些js标识为disk cache,这里就是应用到了缓存。

1563706941053
1563707475189

nginx缓存相关的指令

expires

  • 语法

设置缓存失效时间。

Syntax:expires [modified] time; expires epoch | max | off;
Default:expires off;
Context:http, server, location, if in location

参数说明:

  • max启用后为: Expires: Thu, 31 Dec 2037 23:55:55 GMT Cache-Control: max-age=315360000 (10年)
  • off表示不添加或修改Expires和Cache-Control字段
  • epoch启用后为: Expires: Thu, 01 Jan 1970 00:00:01 GMT Cache-Control: no-cache
  • time 设定具体时间,可以携带单位(通过@) 如: 表示一天内的下午3点30分后失效expires @15h@30m
    • time是负数:表示”Cache-Control: no-cache”
    • time是正数或零:”Cache-Control: max-age=t”,其中t单位为秒

当然expire也可以通过变量来设置,如下所示。我们可以针对不同类型的文件设置不同的缓存时间,详细的配置可以参考h5bp的cache_expiration.conf文件。

map $sent_http_content_type $expires {
    default         off;
    application/pdf 42d;
    ~image/         max;
}

expires $expires;

proxy_cache

  • 语法

启用proxy cache,定义缓存使用的共享内存区域。

Syntax:proxy_cache zone | off;
Default:proxy_cache off;
Context:http, server, location
  • 示例
  proxy_cache cache_zone;

这里,为了方便查看缓存是否命中,可以再header中添加如下指令完成。这里将设置一个X-Proxy-Cache的响应头,upstream_cache_status状态如下:

MISS:未命中缓存

HIT:命中缓存

EXPIRED:缓存已经过期

STALE:命中了陈旧的缓存

UPDATING:内容陈旧,但正在更新

REVALIDATED:Nginx验证了陈旧的内容依然有效

BYPASS:响应是从原始服务器获得

proxy_cache cache_zone;
add_header X-Proxy-Cache $upstream_cache_status;

proxy_cache_path

  • 语法
Syntax:proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size[inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
Default:
Context:http

这里我们介绍几个比较重要的参数,其他参数可以不设置使用默认值即可,详细的可以参考官网文档:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache

  • path:定义缓存文件存放位置
  • levels:定义缓存路径的目录层级,最多三级,每层目录长度为1或2字节
  • keys_zone:name表示共享内存名称,由proxy_cache指令使用;size表示共享内存大小,1mb大约可以存放8000个key
  • inactive:在inactive时间内没有被访问的缓存会被淘汰掉,默认是10分钟

示例

  proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m inactive=60m;

proxy_cache_valid

  • 语法

对不同的响应码缓存不同的时长

Syntax:proxy_cache_valid [code ...] time;
Default:
Context:http, server, location
  • 例子
  proxy_cache_valid 200 302 10m;
  proxy_cache_valid 404      1m;

proxy_cache_methods

  • 语法

对哪些方法进行缓存

Syntax:proxy_cache_methods GET | HEAD | POST ...;
Default:proxy_cache_methods GET HEAD;
Context:http, server, location

proxy_cache_lock_timeout

  • 语法

启用该参数,在同一时间,多个客户端请求缓存未命中时,只有这里面的第一个请求可以发向上游,其他请求要等待第一个响应返回后者超时后,使用缓存响应客户端。该参数可以合并回源请求,减轻峰值流量下的压力。

Syntax:proxy_cache_lock_timeout time;
Default:proxy_cache_lock_timeout 5s;
Context:http, server, location

其示意图如下:

1563706164809

proxy_cache_lock_timeout

  • 语法

等待第一个请求返回的最大时间,到达后直接向上游发送请求,但不缓存响应。配合proxy_cache_lock使用。

Syntax:proxy_cache_lock_age time;
Default:proxy_cache_lock_age 5s;
Context:http, server, location

proxy_cache_lock_age

  • 语法

上一个请求返回相应的超时时间,到达后再放行一个请求发向上游。配合proxy_cache_lock使用。

Syntax:proxy_cache_lock_age time;
Default:proxy_cache_lock_age 5s;
Context:http, server, location

proxy_cache_use_stale

  • 语法

确定在代理服务器通信期间,哪些情况可以使用过时的缓存响应。 该参数可以减少回源请求。

Syntax:proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 |http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | off ...;
Default:proxy_cache_use_stale off;
Context:http, server, location
  • 参数说明:
  • updating:当缓存内容过期,有一个请求正在访问上游试图更新缓存时,其他请求直接使用过期内容返回客户端
  • error:当与上游建立连接、发送请求、读取响应头部等情况出错时,使用缓存
  • timeout:当与上游建立连接、发送请求、读取响应头部等情况出现超时,使用缓存
  • http_xxx:缓存以上错误响应吗的内容 原理如下图所示:

proxy_cache_min_uses

  • 语法

设置请求多少次后可以缓存响应的内容,默认值为1。通过设置该值,可以确保哪些经常被访问的内容才会被添加到缓存中。

Syntax:proxy_cache_min_uses number;
Default:proxy_cache_min_uses 1;
Context:http, server, location

proxy_cache_lock

  • 语法

启用该参数时,当多个客户端请求一个缓存中不存在的文件是(MISS),只有这些请求的第一个允许被发送至服务器,其他请求在第一个请求得到相应内容后再通过缓存文件得到返回结果。如果不启用proxy_cache_lock,则所有在缓存中找不到文件的请求都会直接与服务器通信,从而会增加服务器的负载。

Syntax:proxy_cache_lock on | off;
Default:proxy_cache_lock off;
Context:http, server, location

etag

  • 语法

是否启用服务器资源的唯一标识,用于对比缓存是否发生变化。

Syntax:etag on | off;
Default:etag on;
Context:http, server, location

HTTP缓存机制

HTTP的缓存流程如下图所示。

缓存可以分为强制缓存和对比缓存。

1563695744506

强制缓存

浏览器不会向服务器发送任何请求,直接从本地缓存中读取缓存数据并返回200状态码,如下图所示。如果缓存过期再找服务器。其过程如下:

1563709868078
1563706941053

可以造成强制缓存的字段有如下:

Expires

  • 位置 :HTTP Response Header
  • 说明 :Expires是服务端返回的到期时间。如果下一次请求如果小于服务端返回的过期时间,则直接使用缓存数据。Expires是HTTP1.0的东西,现在浏览器默认都是使用HTTP1.1。而且由于该值是有服务端生成,而客户端的时间和服务端的时间有可能不一致,导致存在一定误差。所以HTTP1.1使用Cache-Control替代
  • 例子
  Expires: Mon, 22 Jul 2019 11:08:59 GMT

Cache-Control

  • 位置:HTTP Response Header
  • 说明:缓存策略定义。
  • max-age:标识资源能够被缓存的最大时间。
  • public:表示该响应任何中间人,包括客户端和代理服务器都可以缓存。
  • private:表示该响应只能用于浏览器私有缓存中,中间人(代理服务器)不能缓存此响应。
  • no-cache:需要使用对比缓存(Last-Modified/If-Modified-Since和Etag/If-None-Match)来验证缓存数据。
  • no-store:所有内容都不会缓存,强制缓存和对比缓存都不会触发。

对比缓存

浏览器在第一次请求数据时,服务器会将缓存的标识与数据一起返回给浏览器,浏览器将这两个缓存到本地缓存数据库中。

再次请求数据时,就会在请求header中带上缓存的标识发送给服务器,服务器根据缓存标识对比,如果发生变化,则返回200状态码,返回完整的响应数据给浏览器,如果未发生更新,则返回304状态码告诉浏览器继续使用缓存数据。

1563710076061

如下图比较所示,在第一次请求时,没有使用缓存。而在第二次使用缓存时,可以明显看到两者请求的时间不一样,后者时间少很多。这是因为服务端如果进行缓存比较后发现未更新,只返回header部分,并返回304状态码通知客户端使用本地缓存,没有将报文的body部分返回给浏览器,所以请求时间和报文大小才明显优化。别小看这几十毫秒,当访问量很大时,这里就优化了很多时间、减少了很多服务器压力。

第一次访问,未使用缓存:

1563710218143

第二次访问,使用缓存:

1563710248218

HTTP请求和响应报文结构如下图所示:

1563710986359

会造成对比缓存的字段如下:

Last-Modified与If-Modified-Since

Last-Modified

  • 位置 :HTTP Response Header
  • 说明 :第一次请求时,服务器会在响应头里设置该参数,告诉浏览器该资源的最后修改时间。

例子

  Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT

If-Modified-Since

  • 位置 :HTTP Request Header
  • 说明 :再次(注意不是第一次)请求服务器时,客户端浏览器通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间。服务器收到请求后,发现header中有If-Modified-Since字段,则与被请求资源的最后修改时间进行对比。 若资源的最后修改时间大于If-Modified-Since,则说明资源被修改过,则响应返回完整的内容,返回状态码200。 若资源的最后修改时间小于或等于If-Modified-Since,则说明资源未修改,则返回304状态码,告诉浏览器继续使用所保存的缓存数据。

Etag与If-None-Match

优先级高于Last-Modified与If-Modified-Since

Etag

  • 位置 :HTTP Response Header
  • 说明 :服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(由服务端生成)。

例子

1563708306527

If-None-Match

  • 位置 :HTTP Request Header
  • 说明 :再次请求服务器时,通过此字段通知服务器客户端缓存的资源的唯一标识。服务器收到请求header周发现有If-None-Match字段,则与被请求资源的唯一标识进行对比。 如果不一样,说明资源被修改过,则返回完整的响应,状态码200。 如果一样,说明资源未被修改过,则返回304状态码,告诉浏览器继续使用缓存的数据。

Nginx緩存配置实践

配置文件如下:

user  nginx;
pid /run/nginx.pid;
worker_processes  auto;
worker_rlimit_nofile 100000;

events {
    worker_connections  2048;
    multi_accept on;
}

http {
    sendfile on;

    access_log off;
    error_log  /data/log/nginx-1.0/error.log  error;

    proxy_cache_path /data/nginx-1.0/cache levels=1:2 keys_zone=cache_zone:10m inactive=60m;



    server {
        listen 80;
        server_name localhost;
        root /usr/local/services/nginx-1.0/html/;

        location / {

        }

        location ~.*\.(gif|jpg|png|css|js)(.*) {
            proxy_cache cache_zone;
            proxy_cache_valid 200 302 24h;
            expires 1d;
            add_header X-Proxy-Cache $upstream_cache_status;
        }
    }
}

测试:

[root@VM_16_4_centos conf]# curl -I http://localhost/test.js
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Sun, 21 Jul 2019 12:35:06 GMT
Content-Type: text/plain
Content-Length: 12
Last-Modified: Sun, 21 Jul 2019 12:33:32 GMT
Connection: keep-alive
ETag: "5d345b9c-c"
Expires: Mon, 22 Jul 2019 12:35:06 GMT
Cache-Control: max-age=86400
Accept-Ranges: bytes

我们再以图片为例。

当我们第一次请求http://localhost/google_logo.jpg

1563712848072

服务端返回了该资源的唯一标识Etag

1563712893770

我们第二次请求时:

可以发现http报文的体积和响应实践大大缩减,说明我们的缓存发回了作用。

1563712931501
1563712982887

转载请备注来源: 《Nginx缓存机制详解》 | shuwoom.com

打赏

发表评论

电子邮件地址不会被公开。