怎么理解缓存

发布时间:2021-10-20 16:52:12 作者:iii
来源:亿速云 阅读:117

本篇内容介绍了“怎么理解缓存”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

浏览器缓存

浏览器缓存是指当我们去访问一个网站或者Http服务的时候,服务器可以设置Http的响应头信息,其中如果设置缓存相关的头信息,那么浏览器就会缓存这些数据,下次再访问这些数据的时候就直接从浏览器缓存中获取或者是只需要去服务器中校验下缓存时候有效,可以减少浏览器与服务器之间的网络时间的开销以及节省带宽。

> Htpp相关的知识,欢迎去参观 《面试篇》Http协议

Cache-Control

该命令是通用首部字段(请求首部和响应首部都可以使用),用于控制缓存的工作机制,该命令参数稍多,常用的参数:

Expires

控制资源失效的日期,当浏览器接受到Expires之后,浏览器都会使用本地的缓存,在过期日期之后才会向务器发送请求;如果服务器同时在响应头中也指定了Cache-Controlmax-age指令时,浏览器会优先处理max-age。 如果服务器不想要让浏览器对资源缓存,可以把Expires和首部字段Date设置相同的值

Last-Modified / If-Modified-Since

Last-Modified

怎么理解缓存

Last-Modified 用于指明资源最终被修改的时间。配合If-Modified-Since一起使用可以通过时间对缓存是否有效进行校验;后面实战会使用到这种方式。

If-Modified-Since

怎么理解缓存

怎么理解缓存

如果请求头中If-Modified-Since的日期早于请求资源的更新日期,那么服务会进行处理,返回最新的资源;如果If-Modified-Since指定的日期之后请求的资源都未更新过,那么服务不会处理请求并返回304 Mot Modified的响应,表示缓存的文件有效可以继续使用。

实战事例

使用SpringMVC做缓存的测试代码:

@ResponseBody
@RequestMapping("/http/cache")
public ResponseEntity<string> cache(@RequestHeader(value = "If-Modified-Since", required = false)
                                            String ifModifiedSinceStr) throws ParseException {

    DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
    Date ifModifiedSince = dateFormat.parse(ifModifiedSinceStr);

    long lastModifiedDate = getLastModifiedDate(ifModifiedSince);//获取文档最后更新时间
    long now = System.currentTimeMillis();
    int maxAge = 30; //数据在浏览器端缓存30秒

    //判断文档是否被修改过
    if (Objects.nonNull(ifModifiedSince) &amp;&amp; ifModifiedSince.getTime() == lastModifiedDate) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Date", dateFormat.format(new Date(now))); //设置当前时间
        headers.add("Expires", dateFormat.format(new Date(now + maxAge * 1000))); //设置过期时间
        headers.add("Cache-Control", "max-age=" + maxAge);
        return new ResponseEntity&lt;&gt;(headers, HttpStatus.NOT_MODIFIED);
    }

    //文档已经被修改过
    HttpHeaders headers = new HttpHeaders();
    headers.add("Date", dateFormat.format(new Date(now))); //设置当前时间
    headers.add("Last-Modified", dateFormat.format(new Date(lastModifiedDate))); //设置最近被修改的日期
    headers.add("Expires", dateFormat.format(new Date(now + maxAge * 1000))); //设置过期时间
    headers.add("Cache-Control", "max-age=" + maxAge);

    String responseBody = JSON.toJSONString(ImmutableMap.of("website", "https://silently9527.cn"));
    return new ResponseEntity&lt;&gt;(responseBody, headers, HttpStatus.OK);

}

//获取文档的最后更新时间,方便测试,每15秒换一次;去掉毫秒值
private long getLastModifiedDate(Date ifModifiedSince) {
    long now = System.currentTimeMillis();

    if (Objects.isNull(ifModifiedSince)) {
        return now;
    }

    long seconds = (now - ifModifiedSince.getTime()) / 1000;
    if (seconds &gt; 15) {
        return now;
    }
    return ifModifiedSince.getTime();
}
  1. 当第一次访问http://localhost:8080/http/cache的时候,我们可以看到如下的响应头信息:

怎么理解缓存

前面我们已提到了Cache-Control的优先级高于Expires,实际的项目中我们可以同时使用,或者只使用Cache-ControlExpires的值通常情况下都是系统当前时间+缓存过期时间

  1. 当我们在15秒之内再次访问http://localhost:8080/http/cache会看到如下的请求头:

怎么理解缓存

此时发送到服务器端的头信息If-Modified-Since就是上次请求服务器返回的Last-Modified,浏览器会拿这个时间去和服务器校验内容是否发送了变化,由于我们后台程序在15秒之内都表示没有修改过内容,所以得到了如下的响应头信息

怎么理解缓存

响应的状态码304,表示服务器告诉浏览器,你的缓存是有效的可以继续使用。

If-None-Match / ETag

If-None-Match

怎么理解缓存

请求首部字段If-None-Match传输给服务器的值是服务器返回的ETag值,只有当服务器上请求资源的ETag值与If-None-Match不一致时,服务器才去处理该请求。

ETag

响应首部字段ETag能够告知客服端响应实体的标识,它是一种可将资源以字符串的形式做唯一标识的方式。服务器可以为每份资源指定一个ETag值。当资源被更新时,ETag的值也会被更新。通常生成ETag值的算法使用的是md5。

ETag: W/"etag-xxxx"

通常建议选择弱ETag值,因为大多数时候我们都会在代理层开启gzip压缩,弱ETag可以验证压缩和不压缩的实体,而强ETag值要求响应实体字节必须完全一致。

实战事例
@ResponseBody
@RequestMapping("/http/etag")
public ResponseEntity<string> etag(@RequestHeader(value = "If-None-Match", required = false)
                                           String ifNoneMatch) throws ParseException {
    long now = System.currentTimeMillis();
    int maxAge = 30; //数据在浏览器端缓存30秒

    String responseBody = JSON.toJSONString(ImmutableMap.of("website", "https://silently9527.cn"));
    String etag = "W/\"" + MD5Encoder.encode(responseBody.getBytes()) + "\""; //弱ETag值

    if (etag.equals(ifNoneMatch)) {
        return new ResponseEntity&lt;&gt;(HttpStatus.NOT_MODIFIED);
    }

    DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
    HttpHeaders headers = new HttpHeaders();
    headers.add("ETag", etag);
    headers.add("Date", dateFormat.format(new Date(now))); //设置当前时间
    headers.add("Cache-Control", "max-age=" + maxAge);

    return new ResponseEntity&lt;&gt;(responseBody, headers, HttpStatus.OK);
}

ETag是用于发送到服务器端进行内容变更验证的,第一次请求http://localhost:8080/http/etag,获取到的响应头信息:

怎么理解缓存

在30秒之内,我们再次刷新页面,可以看到如下的请求头信息:

怎么理解缓存

这里的If-None-Match就是上一次请求服务返回的ETag值,服务器校验If-None-Match值与ETag值相等,所以返回了304告诉浏览器缓存可以用。

怎么理解缓存

ETag与Last-Modified两者应该如何选择?

通过上面的两个事例我们可以看出ETag需要服务器先查询出需要响应的内容,然后计算出ETag值,再与浏览器请求头中If-None-Match来比较觉得是否需要返回数据,对于服务器来说仅仅是节省了带宽,原本应该服务器调用后端服务查询的信息依然没有被省掉;而Last-Modified通过时间的比较,如果内容没有更新,服务器不需要调用后端服务查询出响应数据,不仅节省了服务器的带宽也降低了后端服务的压力;

怎么理解缓存

怎么理解缓存

对比之后得出结论:通常来说为了降低后端服务的压力ETag适用于图片/js/css等静态资源,而类似用户详情信息需要调用后端服务的数据适合使用Last-Modified来处理

Nginx

通常情况下我们都会使用到Nginx来做反向代理服务器,我们可以通过缓冲、缓存来对Nginx进行调优,本篇我们就从这两个方面来聊聊Nginx调优

缓冲

默认情况下,Nginx在返回响应给客户端之前会尽可能快的从上游服务器获取数据,Nginx会尽可能的将上有服务器返回的数据缓冲到本地,然后一次性的全部返回给客户端,如果每次从上游服务器返回的数据都需要写入到磁盘中,那么Nginx的性能肯定会降低;所以我们需要根据实际情况对Nginx的缓存做优化。

1)如果完整数据大小小于busy_buffer大小,当数据传输完成后,马上传给客户端;

2)如果完整数据大小不小于busy_buffer大小,则装满busy_buffer后,马上传给客户端;

典型是设置成proxy_buffers的两倍。

怎么理解缓存

Nginx代理缓冲的设置都是作用到每一个请求的,想要设置缓冲区的大小到最佳状态,需要测量出经过反向代理服务器器的平均请求数和响应的大小;proxy_buffers指令的默认值 8个 4KB 或者 8个 8KB(具体依赖于操作系统),假如我们的服务器是1G,这台服务器只运行了Nginx服务,那么排除到操作系统的内存使用,保守估计Nginx能够使用的内存是768M

  1. 每个活动的连接使用缓冲内存:8个4KB = 8 * 4 * 1024 = 32768字节

  2. 系统可使用的内存大小768M: 768 * 1024 * 1024 = 805306368字节

  3. 所以Nginx能够同时处理的连接数:805306368 / 32768 = 24576

经过我们的粗略估计,1G的服务器只运行Nginx大概可以同时处理24576个连接。

假如我们测量和发现经过反向代理服务器响应的平均数据大小是 900KB , 而默认的 8个4KB的缓冲区是无法满足的,所以我们可以调整大小

http {
    proxy_buffers 30 32k;
}

这样设置之后每次请求可以达到最快的响应,但是同时处理的连接数减少了,(768 * 1024 * 1024) / (30 * 32 * 1024)=819个活动连接;

如果我们系统的并发数不是太高,我们可以将proxy_buffers缓冲区的个数下调,设置稍大的proxy_busy_buffers_size加大往客户端发送的缓冲区,以确保Nginx在传输的过程中能够把从上游服务器读取到的数据全部写入到缓冲区中。

http {
    proxy_buffers 10 32k;
    proxy_busy_buffers_size 64k;
}

缓存

怎么理解缓存

Nignx除了可以缓冲上游服务器的响应达到快速返回给客户端,它还可以是实现响应的缓存,通过上图我们可以看到

Nginx的缓存常用配置:

设置所有的响应被缓存后最大不被访问的存活时间6小时,缓存的大小设置为1g,缓存的有效期是1天,配置如下:

http {
    proxy_cache_path /export/cache/proxy_cache keys_zone=CACHE:10m levels=1:2 inactive=6h max_size=1g;
    server {
        location / {
            proxy_cache CACHE; //指定存放响应到CACHE这个缓存中
            proxy_cache_valid 1d; //所有的响应状态码都被缓存1d
            proxy_pass: http://upstream;
        }
    }
}

如果当前响应中设置了Set-Cookie头信息,那么当前的响应不会被缓存,可以通过使用proxy_ignore_headers来忽略头信息以达到缓存

proxy_ignore_headers Set-Cookie

如果这样做了,我们需要把cookie中的值作为proxy_cache_key的一部分,防止同一个URL响应的数据不同导致缓存数据被覆盖,返回到客户端错误的数据

proxy_cache_key "$host$request_uri$cookie_user"

注意,这种情况还是有问题,因为在缓存的key中添加cookie信息,那么可能导致公共资源被缓存多份导致浪费空间;要解决这个问题我们可以把不同的资源分开配置,比如:

server {
    proxy_ignore_headers Set-Cookie;
    
    location /img {
        proxy_cache_key "$host$request_uri";
        proxy_pass http://upstream;
    }
    
    
    location / {
        proxy_cache_key "$host$request_uri$cookie_user";
        proxy_pass http://upstream;
    }
}

清理缓存

虽然我们设置了缓存加快了响应,但是有时候会遇到缓存错误的请求,通常我们需要为自己开一个后面,方便发现问题之后通过手动的方式及时的清理掉缓存。Nginx可以考虑使用ngx_cache_purge模块进行缓存清理。

location ~ /purge/.* {
    allow 127.0.0.1;
    deny all;
    proxy_cache_purge cache_one $host$1$is_args$args
}

该方法要限制访问权限proxy_cache_purge缓存清理的模块,cache_one指定的key_zone,$host$1$is_args$args 指定的生成缓存key的参数

存储

如果有大的静态文件,这些静态文件基本不会别修改,那么我们就可以不用给它设置缓存的有效期,让Nginx直接存储这些文件直接。如果上游服务器修改了这些文件,那么可以单独提供一个程序把对应的静态文件删除。

http {
    proxy_temp_path /var/www/tmp;
    
    server {
        root /var/www/data;
        
        location /img {
            error_page 404 = @store
        }
        
        location @store {
            internal;
            proxy_store on;
            proxy_store_access group:r all:r;
            proxy_pass http://upstream;
        }
    }
}

请求首先会去/img中查找文件,如果不存在再去上游服务器查找;internal 指令用于指定只允许来自本地 Nginx 的内部调用,来自外部的访问会直接返回 404 not found 状态。proxy_store表示需要把从上游服务器返回的文件存储到 /var/www/dataproxy_store_access设置访问权限

“怎么理解缓存”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

推荐阅读:
  1. 如何理解Php output buffering缓存及程序缓存
  2. 如何理解Nginx + PHP缓存

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

nginx

上一篇:怎么进行echart Coordinate内容解码

下一篇:Hadoop的I/O 操作是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》