缓存静态资源,不知怎么解决

供稿:hz-xin.com     日期:2025-01-21
为什么要把静态资源放在WEB-INF下

之前看过apach及nginx对于静态资源(含js,图片,css等)部分的缓存,用于加速并减轻后台实际web服务器的压力。 静态资源缓存是WEB服务器优化的一种手段,基本原理如下: 1.客户端浏览器请求服务器一个服务(该服务含有图片,js等静态资源),通常...

使用静态页缓存提升网站性能,变化很少的静态资源可以设置客户端缓存时间,减少请求。
如果一定要不要静态页缓存,可以设置页面不缓存静态页面,这个要根据实际的WEB应用服务进行设置了。

之前看过apach及nginx对于静态资源(含js,图片,css等)部分的缓存,用于加速并减轻后台实际web服务器的压力。
静态资源缓存是WEB服务器优化的一种手段,基本原理如下:
1.客户端浏览器请求服务器一个服务(该服务含有图片,js等静态资源),通常会对于每一个网页中的独立图片或js文件发送一个http请求
2.WEB服务器对于每个资源HTTP请求进行解析,并生成一个资源修改时间的唯一值(可以是etag或last_modified参数),放入服务器端map,key为资源url,value为资源修改时间。最后将此资源修改时间的唯一值包含在http头上返回,因为是首次请求,所以会将所有内容放在http body中一并返回给客户浏览器端
3.客户浏览器接收服服务器响应,并将服务器返回的资源修改时间作为key放入浏览器客户端,value为http body中的实际资源内容
4.客户浏览器再次请求静态资源时,会将资源修改时间一并发送给服务器
5.服务端会从最新的map中取出该资源url对应的修改时间,如果值晚于客户端请求的资源修改时间,这时会返回最新的已经修改过的资源给客户端。否则返回304 not modifed

这里记录资源修改时间的方式有etag及last_modified。最先有的是last_modified,它的工作方式就是上述介绍的,但缺点是只能精确到秒级别。也就是说当你在一秒中修改资源两次,而客户端拿到的是第一次修改,那之后就算客户端第二次再次请求也不会拿到最新的资源。
而etag的出现正是为了解决last_modified的秒级问题,于http 1.1被提出。

今天测试了下,在没有nginx等前端反向代理服务器时,tomcat竟然默认对静态资源做了缓存。
tomcat默认运用etag及last_modifed。etag与if_no_match(客户端浏览器上传时在http head中应该放的属性名)一起使用,last_modified与If-Modified-Since一起使用。

客户端首次请求时,得到请求响应数据如下:

GET http://localhost:8080/webTest/jsp/index.jsp [HTTP/1.1 200 OK 1ms]
GET http://localhost:8080/webTest/js/hello.js [HTTP/1.1 200 OK 1ms]
GET http://localhost:8080/webTest/img/a.jpg [HTTP/1.1 200 OK 2ms]
我们看一下Hello.js这个请求响应具体信息:
server Apache-Coyote/1.1 (表明服务器是tomcat)
Last-Modified: Sun, 11 May 2014 10:54:33 GMT
Etag: W/"175-1399805673000"
Date: Sun, 11 May 2014 10:59:23 GMT
Content-Type: application/javascript;charset=UTF-8
Content-Length: 175
Accept-Ranges: bytes

从上面可以看到tomcat即返回了last_modified也返回了etag。

客户端再次请求时,请求数据如下:
If-None-Match: W/"175-1399805673000"
If-Modified-Since: Sun, 11 May 2014 10:54:33 GMT
响应如下:

GET http://localhost:8080/webTest/jsp/index.jsp [HTTP/1.1 200 OK 1ms]
GET http://localhost:8080/webTest/js/hello.js [HTTP/1.1 304 Not Modified 1ms]
GET http://localhost:8080/webTest/img/a.jpg [HTTP/1.1 304 Not Modified 1ms]
从中我们可以看到tomcat对于静态数据作了缓存。

接着我们分析tomcat对于这部分静态缓存的判断处理,这部分逻辑是写在DefaultServlet类中,
我们可以在doGet方法中进入ServiceContext方法中找到以下源码:
// Check if the conditions specified in the optional If headers are
// satisfied.
if (cacheEntry.context == null) {

// Checking If headers
boolean included =
(request.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null);
if (!included
&& !checkIfHeaders(request, response, cacheEntry.attributes)) { //这句判断是否需要返回整个资源请求
return;
}

}
上面源码的 if (!included
&& !checkIfHeaders(request, response, cacheEntry.attributes))
用于判断是否需要返回整个资源,如果indcluded与checkIfHeaders方法返回的都是false,这时就直接返回,说明资源未修改,或者是缓存不支持的请求方式。
我们接着查看checkIfHeaders方法:
/**
* Check if the conditions specified in the optional If headers are
* satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceAttributes The resource information
* @return boolean true if the resource meets all the specified conditions,
* and false if any of the conditions is not satisfied, in which case
* request processing is stopped
*/
protected boolean checkIfHeaders(HttpServletRequest request,
HttpServletResponse response,
ResourceAttributes resourceAttributes)
throws IOException {

return checkIfMatch(request, response, resourceAttributes)
&& checkIfModifiedSince(request, response, resourceAttributes)
&& checkIfNoneMatch(request, response, resourceAttributes)
&& checkIfUnmodifiedSince(request, response, resourceAttributes);

}

可以看到tomcat只有当这四个属性全部返回true(也就是说全部认为资源已经改变)才会返回true,这样最终会将整个资源(最新修改过的)返回客户端。
在这里,我们从上面实际过程当中看到,浏览器第二次请求资源时在http请求header中放了
If-None-Match: W/"175-1399805673000"
If-Modified-Since: Sun, 11 May 2014 10:54:33 GMT
这两个属性。
因此我们查看
&& checkIfModifiedSince(request, response, resourceAttributes)
&& checkIfNoneMatch(request, response, resourceAttributes)
这两个方法
checkIfModifiedSince源码如下:
/**
* Check if the if-modified-since condition is satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceInfo File object
* @return boolean true if the resource meets the specified condition,
* and false if the condition is not satisfied, in which case request
* processing is stopped
*/
protected boolean checkIfModifiedSince(HttpServletRequest request,
HttpServletResponse response,
ResourceAttributes resourceAttributes) {
try {
long headerValue = request.getDateHeader("If-Modified-Since");
long lastModified = resourceAttributes.getLastModified();
if (headerValue != -1) {

// If an If-None-Match header has been specified, if modified since
// is ignored.
if ((request.getHeader("If-None-Match") == null)
&& (lastModified < headerValue + 1000)) {
// The entity has not been modified since the date
// specified by the client. This is not an error case.
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", resourceAttributes.getETag());

return false;
}
}
} catch (IllegalArgumentException illegalArgument) {
return true;
}
return true;

}
源码中可以看到:
if ((request.getHeader("If-None-Match") == null)
&& (lastModified < headerValue + 1000)) {
这句话表明只有在客户端浏览器发送的请求头中不包含If-None-Match,IfModifiedSince才会生效。
我们接着看checkIfNoneMatch,源码如下:
/**
* Check if the if-none-match condition is satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceInfo File object
* @return boolean true if the resource meets the specified condition,
* and false if the condition is not satisfied, in which case request
* processing is stopped
*/
protected boolean checkIfNoneMatch(HttpServletRequest request,
HttpServletResponse response,
ResourceAttributes resourceAttributes)
throws IOException {

String eTag = resourceAttributes.getETag();
String headerValue = request.getHeader("If-None-Match");
if (headerValue != null) {

boolean conditionSatisfied = false;

if (!headerValue.equals("*")) {

StringTokenizer commaTokenizer =
new StringTokenizer(headerValue, ",");

while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
String currentToken = commaTokenizer.nextToken();
if (currentToken.trim().equals(eTag))
conditionSatisfied = true;
}

} else {
conditionSatisfied = true;
}

if (conditionSatisfied) {

// For GET and HEAD, we should respond with
// 304 Not Modified.
// For every other method, 412 Precondition Failed is sent
// back.
if ( ("GET".equals(request.getMethod()))
|| ("HEAD".equals(request.getMethod())) ) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", eTag);

return false;
}
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
return true;

}
这里:
String eTag = resourceAttributes.getETag();
String headerValue = request.getHeader("If-None-Match");
这两句比较简单,就是分别从服务器缓存和http请求头中中取出etag。
接着判断这两个etag如果相等,则conditionSatisfied为true,会执行到以下语句:
if (conditionSatisfied) {

// For GET and HEAD, we should respond with
// 304 Not Modified.
// For every other method, 412 Precondition Failed is sent
// back.
if ( ("GET".equals(request.getMethod()))
|| ("HEAD".equals(request.getMethod())) ) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", eTag);

return false;
}
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
这段语句中可以发现,如果资源未改变的情况下,并且请求方式为GET或者HEAD时,会返回304状态码。否则返回一个412状态码,同样不会返回资源内容。
如果上述最终
if ((request.getHeader("If-None-Match") == null)
&& (lastModified < headerValue + 1000))
条件不成立,即资源更新了或者是第一次请求,这里会读取当前请求资源文件,并最终放入http响应中。

缓存静态资源,不知怎么解决
1.客户端浏览器请求服务器一个服务(该服务含有图片,js等静态资源),通常会对于每一个网页中的独立图片或js文件发送一个http请求 2.WEB服务器对于每个资源HTTP请求进行解析,并生成一个资源修改时间的唯一值(可以是etag或last_modified参数),放入服务器端map,key为资源url,value为资源修改时间。最后将此资源修改时间的...

微信小程序web-view缓存问题及解决方案
和页面缓存一样,在服务器中配置客户端不缓存静态资源。但是这样的话会导致每次请求页面,所有静态资源都会重新从服务器获取,这样对用户体验(打开页面的速度、宽带的占用)、服务器的压力都非常的不友好。所以不建议使用这种方式。直接使用版本号的方式即可。最终解决方案: 根据上面的分析,及多种解决方案的尝试,可以发现,...

djangoadmin怎么配置使用(djangoadmin)
4.STATICFILES_DIRS:在实际项目开发中,如果我们的静态文件存放在一个或多个目录时,可以定义一个列表,用于存储静态文件,django默认配置并没有这个配置,如果需要,请自己定义,当项目下存在一个或多个静态资源的目录时,非常适合这样定义。注意,这个配置只在debug=true时生效(开发环境)8.templates:定...

一文摸清前端监控实践要点(一)性能监控
如果想获取资源的具体时间,跨域资源需要设置响应头Timing-Allow-Origin 对于可控跨域资源例如自家CDN,Timing-Allow-Origin的响应头origins至少得设置了主页面的域名,允许获取资源时间 一般对外公共资源设置为Timing-Allow-Origin:*。分析得出的指标 分析得出的指标,意味着不是一次采集就能得到的指标数据,而指的是对于整个应...

vue跨域问题的三种解决方案?
1.target是你要代理的域名,必须要加上http。2.这里用'\/api'代替target里面的地址,组件中调用接口时直接用'\/api'代替。比如我要调用'.直接写'\/api\/news'即可 3.changeOrigin必须为true 在页面中请求写法如下:开心吧。数据终于拿到啦!!!我们知道,vue2.0项目中的static文件夹存储的是静态资源,...

Nginx静态资源传输优化,文件高效传输,事半功倍
Tips:通常不会使用*进行匹配,如果所有的类型都开启压缩,如果本身静态资源的可压缩性不高,就会造成系统资源的浪费。到这里,我们可以来体验一下开启gzip之后的压缩效果。1)准备静态文件\/usr\/local\/nginx\/html\/jquery.js 下载地址: code.jquery.com\/jquery-...2)增加location块 访问 192.168.110....

IP代理网站中的透明代理指的是什么?
1、可以在代理服务器上设置缓存,缓存命中就可以直接访问,不需要再去访问目标服务器。如果有独立的缓存服务器,也可以是在代理服务器上先去缓存服务器上查询缓存,查不到在将请求转发给目标服务器。2、在代理服务器上缓存静态资源,静态资源一般是不会改变的。就不用去目标服务器上拿了。

使用VUE开发的项目怎么解决SEO问题最好
使用Vue开发项目时,由于Vue依赖JavaScript来动态加载和渲染数据,这导致搜索引擎无法直接获取到页面上的内容,从而影响SEO优化。解决这个问题的方法多样,包括利用CDN缓存静态文件,这能够帮助搜索引擎更快地获取页面的静态资源,从而提高页面的抓取效率。另一种方法是通过服务端对SEO部分进行动态赋值。这种方法的...

shiro更新session次数很多怎么解决
用shiro时发现sessiondao.update等方法调用频繁.原因:session update大多是在更新最后访问时间.每一次请求都会更新最后访问时间.当一个页面包含多个资源的时候就会发生多次update session.解决方案1:重写sessiondao的update方法,当发现是静态资源时,放弃update 方案2:将SpringMVC和Shiro的url-pattern由"\/"修改为...

网站数据多,传输速度慢,怎么解决?
网站数据传输速度慢的问题可以通过多种方法来解决,以下是一些有效的优化策略:1. 优化Web设计:例如,优化图片大小、代码格式和外部脚本的使用,选择优质的服务提供商进行托管、内容缓存和负载平衡。2. 测试网站性能:使用性能测量工具,如Google Lighthouse或Cloudflare Observatory,来评估网站性能...