Nginx 缓存

153 阅读3分钟

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战

服务器协商缓存

上一篇文章 我们学习了浏览器缓存机制以及浏览器如何判断是否使用缓存。使用浏览器缓存主要根据响应头参数 expires 和 cache-control,如果浏览器缓存过期了,那么会由服务器协商是否使用服务器缓存,一般将这种服务器缓存称为协商缓存。

image.png

1. 为什么需要协商缓存?

因为浏览器缓存只是针对使用这个浏览器的用户来说,提高了它的网站性能,而服务器缓存可以减少所有用户访问这个网站的通信时间。其次,协商缓存可以通过判断内容是否更新来决定是否使用缓存。浏览器缓存,我们只是给它设置了一个过期时间。

2. 协商缓存相关参数有哪些?

if-modified-since 和 last-modified

if-modified-since 和 last-modified 是一对的。last-modified 是服务器返回的响应头参数,表示请求的文件上一次修改的时间。if-modified-since 是浏览器请求头参数,询问在这个时间后服务器是否修改。

举个例子更容易理解。第一次请求资源,没有缓存,返回 200 和静态资源,响应头参数有 last-modified。第二次请求资源,这里假设不使用浏览器缓存,那么浏览器会向服务器请求,将上次访问得到的 last-modified 的值,作为请求头参数 if-modified-since 的值,服务器判断 if-modified-since 的值和资源的 last-modified 的值相等,说明资源没有更改,返回 304。

if_none_match 和 etag

etag 是资源的唯一标识,请求服务器会返回这个响应头参数,下次访问通过带上 if_none_match 参数,值便是 etag 的值,由服务器判断资源是否已经改变,如果改变了就返回 200,否则返回 304。

Nginx 缓存相关源码

了解了服务器如何决策是要返回 200 还是 304 后,我还想要了解 Nginx 服务器具体的实现思路是怎样的呢?

相关代码及注释如下:

//1. 判断if_modified_since 和 if_none_match 请求头是否存在
if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
        
        //1.1 if_modified_since 或者 if_none_match 请求头存在
        
        //2.1 if_modified_since 存在且判断last_modified,判断不通过,返回200
        if (r->headers_in.if_modified_since
            && ngx_http_test_if_modified(r))
        {
            return ngx_http_next_header_filter(r);
        }

        //2.2 if_none_match 请求头存在且判断etag,判断不通过,返回200
        if (r->headers_in.if_none_match
            && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
        {
            return ngx_http_next_header_filter(r);
        }

        /* not modified */

        //2.3 资源没有修改,返回304
        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
        r->headers_out.status_line.len = 0;
        r->headers_out.content_type.len = 0;
        ngx_http_clear_content_length(r);
        ngx_http_clear_accept_ranges(r);

        if (r->headers_out.content_encoding) {
            r->headers_out.content_encoding->hash = 0;
            r->headers_out.content_encoding = NULL;
        }

        return ngx_http_next_header_filter(r);
    }
    //1.2 if_modified_since 和 if_none_match 请求头不存在,返回200
    return ngx_http_next_header_filter(r);

如果没有 if_modified_since 和 if_none_match 请求头,直接返回 200 和资源,不使用服务器缓存。当有 if_modified_since 和 if_none_match 请求头时, Nginx 先比较 if_modified_since 和 last_modified,Nginx 提供了 if_modified_since 的配置指令,通过配置不同的值,会影响 if_modified_since 和 last_modified 的比较结果。如果 if_modified_since 配置为 off,直接返回 200。如果 if_modified_since 配置为 exact,只要 if_modified_since 和 last_modified 不相等就返回 200。如果if_modified_since 配置为 before,资源的 last_modified_time <if_modified_since 才有可能使用缓存。比较完 if_modified_since 和 last_modified,再继续比较 if_none_match 和 etag。当不含有 if_none_match 头部或者 etag 的值和 if_none_match 相等时,返回 304。

image.png

参考