目录
1. 别人是怎么玩的?
2.1. 知乎
2.2. 京东
2. 缓存作用?
3. 缓存分类?
4. 缓存原理?
4.1. 核心词汇
4.2. 总体流程
4.3. 缓存控制
4.3.1. Cache-Control
4.3.1.1. No caching
4.3.1.2. Cache but revalidate
4.3.1.3. Expiration
4.3.1.4. Cache-Control 配置建议
4.3.2. Pragma
4.4. 缓存验证
4.4.1. LastModified
4.4.2. ETags
4.5 一种更新资源的方式
5. 用户行为与浏览器
5.1. 地址栏访问
5.2. F5 刷新
5.2. Ctrl+F5 刷新
6. FAQ
6.1. HTML Meta Tags and HTTP Headers?
6.2. Pragma HTTP Headers (and why they don’t work)
6.3. 如何清理浏览器缓存?
6.4. Targets of caching operations?
6.5. Tomcat 如何生成 ETag?
6.6. Tomcat 如何对待"If-None-Match"等请求头的?
1. 【知乎】是怎么玩的?
1.1. 知乎
1.2. 京东
2. 缓存作用?
3. 缓存分类?
缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。缓存的种类有很多,其大致可归为两类:
4. 缓存原理?
4.1. 核心词汇
4.2. 总体流程
4.3. 缓存控制
4.3.1. Cache-Control
The Cache-Control HTTP/1.1 general-header field is used to specify directives for caching mechanisms in both requests and responses. Use this header to define your caching policies with the variety of directives it provides.
4.3.1.1. No caching
The cache should not store anything about the client request or server response. A request is sent to the server and a full response is downloaded each and every time.
Cache-Control: no-store
示例:禁止浏览器缓存CSS、JS、PNG、HTML文件
4.3.1.2. Cache but revalidate
A cache will send the request to the origin server for validation before releasing a cached copy.
Cache-Control: no-cache
4.3.1.3. Expiration
The most important directive here is max-age=<seconds>, which is the maximum amount of time in which a resource will be considered fresh. This directive is relative to the time of the request, and overrides the Expires header (if set).
Cache-Control: max-age=31536000
注:若响应中含有Cache-Control:max-age=0或Cache-Control:no-cache 或 Pragma:no-cache,可使浏览器在每次使用缓存前,都去跟服务器确认缓存的可用性;
4.3.1.4. Cache-Control 配置建议
4.3.2. Pragma
4.4. 缓存验证
When a cached document's expiration time has been reached, it is either validated or fetched again. Validation can only occur if the server provided either a strong validator or a weak validator.
4.4.1. Last-Modified
4.4.2. ETags
4.5 一种更新资源的方式
5. 用户行为与浏览器
5.1. 地址栏访问
5.2. F5 刷新
5.3. Ctrl + F5
按Ctrl+F5,浏览器将放弃自身缓存,同时也不会向向服务端确认新鲜度,直接从拉取资源。
6:FAQ?
6.1. HTML Meta Tags and HTTP Headers
6.2. Pragma HTTP Headers (and why they don’t work)
6.3. 如何清理浏览器缓存?
各浏览器都可通过 [Ctrl+Shift+Delete] 快捷键完成缓存清理。
6.4. Targets of caching operations?
HTTP caching is optional but usually desirable. HTTP caches are typically limited to caching responses to GET; they may decline other methods. The primary cache key consists of the request method and target URI (often only the URI is used — this is because only GET requests are caching targets).
6.5. Tomcat 如何生成 ETag?
package org.apache.catalina.webresources
public abstract class AbstractResource implements WebResource {
@Override
public final String getETag() {
if (weakETag == null) {
synchronized (this) {
if (weakETag == null) {
long contentLength = getContentLength();
long lastModified = getLastModified();
if ((contentLength >= 0) || (lastModified >= 0)) {
weakETag = "W/\"" + contentLength + "-" +
lastModified + "\"";
}
}
}
}
return weakETag;
}
}
6.6. Tomcat 如何对待"If-None-Match"等请求头的?
package org.apache.catalina.servlets;
public class DefaultServlet extends HttpServlet {
/**
* 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 resource The resource
* @return <code>true</code> if the resource meets all the specified
* conditions, and <code>false</code> if any of the conditions is not
* satisfied, in which case request processing is stopped
* @throws IOException an IO error occurred
*/
protected boolean checkIfHeaders(HttpServletRequest request,
HttpServletResponse response,
WebResource resource)
throws IOException {
return checkIfMatch(request, response, resource)
&& checkIfModifiedSince(request, response, resource)
&& checkIfNoneMatch(request, response, resource)
&& checkIfUnmodifiedSince(request, response, resource);
}
/**
* Check if the if-match condition is satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
* @return <code>true</code> if the resource meets the specified condition,
* and <code>false</code> if the condition is not satisfied, in which case
* request processing is stopped
* @throws IOException an IO error occurred
*/
protected boolean checkIfMatch(HttpServletRequest request,
HttpServletResponse response, WebResource resource)
throws IOException {
String eTag = resource.getETag();
String headerValue = request.getHeader("If-Match");
if (headerValue != null) {
if (headerValue.indexOf('*') == -1) {
StringTokenizer commaTokenizer = new StringTokenizer
(headerValue, ",");
boolean conditionSatisfied = false;
while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
String currentToken = commaTokenizer.nextToken();
if (currentToken.trim().equals(eTag))
conditionSatisfied = true;
}
// If none of the given ETags match, 412 Precondition failed is
// sent back
if (!conditionSatisfied) {
response.sendError
(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
}
return true;
}
/**
* 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 resource The resource
* @return <code>true</code> if the resource meets the specified condition,
* and <code>false</code> if the condition is not satisfied, in which case
* request processing is stopped
*/
protected boolean checkIfModifiedSince(HttpServletRequest request,
HttpServletResponse response, WebResource resource) {
try {
long headerValue = request.getDateHeader("If-Modified-Since");
long lastModified = resource.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", resource.getETag());
return false;
}
}
} catch (IllegalArgumentException illegalArgument) {
return true;
}
return true;
}
/**
* 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 resource The resource
* @return <code>true</code> if the resource meets the specified condition,
* and <code>false</code> if the condition is not satisfied, in which case
* request processing is stopped
* @throws IOException an IO error occurred
*/
protected boolean checkIfNoneMatch(HttpServletRequest request,
HttpServletResponse response, WebResource resource)
throws IOException {
String eTag = resource.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;
}
/**
* Check if the if-unmodified-since condition is satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
* @return <code>true</code> if the resource meets the specified condition,
* and <code>false</code> if the condition is not satisfied, in which case
* request processing is stopped
* @throws IOException an IO error occurred
*/
protected boolean checkIfUnmodifiedSince(HttpServletRequest request,
HttpServletResponse response, WebResource resource)
throws IOException {
try {
long lastModified = resource.getLastModified();
long headerValue = request.getDateHeader("If-Unmodified-Since");
if (headerValue != -1) {
if ( lastModified >= (headerValue + 1000)) {
// The entity has not been modified since the date
// specified by the client. This is not an error case.
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
} catch(IllegalArgumentException illegalArgument) {
return true;
}
return true;
}
}
参考:
Caching Tutorial: https://www.mnot.net/cache_docs/ Cache-Control for Civilians: https://csswizardry.com/2019/03/cache-control-for-civilians/ Caching best practices & max-age gotchas: https://jakearchibald.com/2016/caching-best-practices/ Web Caching Basics: Terminology, HTTP Headers, and Caching Strategies: https://www.digitalocean.com/community/tutorials/web-caching-basics-terminology-http-headers-and-caching-strategies HTTP: https://www.ietf.org/rfc/rfc2616.txt https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Pragma Cache-Control Header Builder: https://cache-control.sdgluck.now.sh/