Http缓存总结

Http缓存总结

强制缓存与协商缓存

强制缓存:本地缓存

当浏览器二次请求服务器资源时,浏览器直接从本地缓存中读取并返回200,
不会与服务器进行交互

协商缓存:浏览器根据HTTP响应头里面的内容,来采取对应的缓存方式

如果用户访问的资源在上次响应中携带了协商缓存相关的头,
浏览器会携带相关字段并向服务端确认缓存的资源是否有效。
如果服务端判断该资源有效,则返回304,携带相关缓存头但不返回资源内容,
浏览器直接使用本地缓存。
否则返回200,服务器返回新的资源内容。

强制缓存

Expires
Expires是HTTP 1.0出现的响应头,它的值是GMT格式的时间字符串,
如Expires: Sun Dec 24 2022 18:01:30 GMT。这个时间代表资源的过期失效时间,
在这个时间之前,浏览器访问该资源时始终使用强缓存。
缺点:Expires是个相对时间(客户端自己修改了本地时间)缓存失效。

代码实现


@RequestMapping("/v3/{id}.json")
public Map<String,String> show3(@PathVariable("id") String id,HttpServletResponse response) {
		Map<String,String> map = new HashMap<>();
		map.put("id", id);
		map.put("show3 name", "user:"+id);
		/**
		 * 格式化一个GMT(格林尼治时间) 时间
		 * 
		 * 转化后的GMT 有时差问题和本地不一致 但不影响缓存 
		 */
		Date date = new Date(System.currentTimeMillis() + 10000); //10秒后过期
		DateFormat format = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss z", Locale.ENGLISH);
	    format.setTimeZone(TimeZone.getTimeZone("GMT"));
	    String str = format.format(date);
	    response.addHeader("Expires", str);
	    System.out.println("tach server v3@@@@@@@@@@@@@@@@@");
		System.out.println("tach server v3@@@@@@@@@@@@@@@@@");
		return map;
	}
Cache-Control
Cache-Control是HTTP 1.1出现的响应头,它可以用在请求头和响应头中,该指令是单向的,意味着在请求中设置的指令,在响应中可能没有。
常用的值有max-age、no-cache和no-store

代码实现

	@RequestMapping("/v2/{id}.json")
	public Map<String,String> show2(@PathVariable("id") String id,HttpServletResponse response) {
		Map<String,String> map = new HashMap<>();
		map.put("id", id);
		map.put("show2 name", "user:"+id);
		
		/**
		 * 设置过期时间为10秒
		 * 10秒内多次请求 /v2/1.json 不会触发打印tach server v2@@@@@@@@@@@@@@@@@
		 * 因为浏览器没发送请求
		 * 
		 * 注意:
		 * 1 浏览器使用firefox,用chrome 没有效果  
		 * 2 浏览器不能打开调试模式 F12 ,调试模式下max-age无效
		 */
		response.addHeader("Cache-Control", "max-age=10");
		System.out.println("tach server v2@@@@@@@@@@@@@@@@@");
		System.out.println("tach server v2@@@@@@@@@@@@@@@@@");
		return  map;
	}

协商缓存

Last-Modified/If-Modified-Since
Last-Modified代表的是资源的最后修改时间,是一个GMT格式的时间。服务器在返回资源时,如果写到Last-Modified,则浏览器在后续对该资源的访问的请求头里都会携带If-Modified-Since,值与服务器返回的Last-Modified值相同。服务端通过该值来判断浏览器请求的该资源是否有修改,如果有,则返回修改后的内容,且返回200,否则返回304,body中没有内容。

代码实现

@RequestMapping("/v4/{id}.json")
	public Map<String,String> show4(@PathVariable("id") String id,
			HttpServletRequest request,
			HttpServletResponse response) {
		/**
		 * 如果第一次请求中有Last-Modified
		 * 则浏览器在后续对该资源的访问的请求头里都会携带If-Modified-Since
		 * 值与服务器返回的Last-Modified值相同
		 */
		String ifModifiedSince = request.getHeader("If-Modified-Since");
		/**
		 * 第一次请求没有If-Modified-Since
		 */
		boolean doCache = true;
		if(ifModifiedSince == null || ifModifiedSince.trim().equals("") || ifModifiedSince.equals("null")) {
			doCache = false;
		} else {
			/**
			 * 转化下浏览器发送的时间
			 */
			DateFormat format = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss z", Locale.ENGLISH);
		    format.setTimeZone(TimeZone.getTimeZone("GMT"));
		    Date d = null;
			try {
				d = format.parse(ifModifiedSince);
			} catch (ParseException e) {
			}	
		    /**
		     * 当前时间大于缓存过期时间
		     */
		    doCache = System.currentTimeMillis() > d.getTime();
		}
		if(doCache) {
			/**
			 * 返回304并且 body没有内容,减少数据测传输
			 */
			response.setStatus(304);
			return null;
		}else {
			Map<String,String> map = new HashMap<>();
			map.put("id", id);
			map.put("show4 name", "user:"+id + " sys: " + System.currentTimeMillis());
			/**
			 * 格式化一个GMT(格林尼治时间) 时间
			 */
			Date date = new Date(System.currentTimeMillis() + 20000); //20秒后过期
			DateFormat format = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss z", Locale.ENGLISH);
		    format.setTimeZone(TimeZone.getTimeZone("GMT"));
		    String str = format.format(date);
		    response.addHeader("Last-Modified", str);
		    try {
		    	/**
		    	 * 模拟http请求很耗时
		    	 * Last-Modified/If-Modified-Since 可以节约这部分时间 和 减少响应数据包的大小
		    	 */
				Thread.sleep(3000L);
			} catch (InterruptedException e) {
			}
		    return map;
		}
	}
ETag/If-None-Match
服务端需要根据资源做一个hash计算,把hash值作为ETag返回给客户端
后续客户端对该资源的访问的请求头里都会携带If-None-Match,值与服务器返回的ETag值相同。

服务端需要比较值来判断是否缓存有效

代码实现

@RequestMapping("/v5/{id}.json")
	public String show5(@PathVariable("id") String id,
			HttpServletRequest request,
			HttpServletResponse response) {
		
		/**
		 * 准备数据
		 * 数据有效期为1分钟 
		 */
		DateFormat format = new SimpleDateFormat("HH:mm");
	    String str = format.format(new Date()); //一分钟后str会变
	    
		Map<String,String> map = new HashMap<>();
		map.put("id", id);
		map.put("show5 name", "user:"+id+str);
		/**
		 * 业务数据
		 */
		String data = JSON.toJSONString(map);
		
		/**
		 * 对返回的内容做hash计算
		 * 
		 * ETag 的问题是服务期必须要对数据做处理,这会消耗服务器的cpu
		 */
		String md5str = DigestUtils.md5Hex(data);
		
		/**
		 * 如果第一次请求中有ETag
		 * 则浏览器在后续对该资源的访问的请求头里都会携带If-None-Match
		 * 值与服务器返回的ETag值相同
		 */
		String IfNoneMatch = request.getHeader("If-None-Match");
		/**
		 * 第一次请求没有If-Modified-Since
		 */
		boolean doCache = true;
		if(IfNoneMatch == null || IfNoneMatch.trim().equals("") || IfNoneMatch.equals("null")) {
			doCache = false;
		} else {
		    /**
		     * 当前时间大于缓存过期时间
		     */
		    doCache =  md5str.equals(IfNoneMatch);
		}
		if(doCache) {
			/**
			 * 返回304并且 body没有内容,减少数据测传输
			 */
			response.setStatus(304);
			return null;
		}else {
		    response.addHeader("ETag", md5str);
		    return data;
		}
	}

总结

  • Cache-Control: 浏览器直接使用本地缓存不发送http请求
  • Expires:览器直接使用本地缓存不发送http请求,蛋可能会因为客户端修改本地时间后导致缓存失效
  • Last-Modified/If-Modified-Since: 浏览器还是会发送网络请求,服务端返回304和空的body,减少了网络传输包的大小
  • ETag/If-None-Match:浏览器还是会发送网络请求,服务端返回304和空的body,但是服务端需要计算数据hash后是否与If-None-Match一致,这里还是会消耗资源

注意测试的时候浏览器不要F5强刷,F12不要勾选禁用缓存 firefox效果比较明显

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寂寞的4角钱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值