HTTP headers
当前后端通信时,会互发HTTP报文,HTTP报文由header、start line、body组成。
HTTP 标头(header)允许客户端和服务器通过 HTTP 请求(request)或者响应(response)传递附加信息。
常见header字段
Authorization 包含用于向服务器验证用户代理身份的凭据。
Cache-Control 请求和响应中缓存机制的指令。(reponse header)
Expires 响应被视为过时的日期/时间。(reponse header)
Last-Modified 资源的最后修改日期,用于比较同一个资源的多个版本。它不如 ETag 准确,但在某些环境中更容易计算。使用 If-Modified-Since 和 If-Unmodified-Since 的条件请求使用此值来更改请求的行为。
缓存和HTTP headers:
缓存实际上是通过前后端交互时,设置的header字段不同,来实现不同的缓存效果。
缓存
缓存思想:
在前端向后端首次发送请求后,保存一份请求资源的响应副本,当前端再次请求相同的资源时,如果判断缓存命中则拦截请求,将之前存储的响应副本返回,从而避免重新向后端服务器发起重复的资源请求。
缓存优点:
减少冗余的数据传输;减少服务器负担;提升网站性能;加快客户端页面加载的速度(性能优化)
缓存分类:
共享缓存 响应数据可以被所有用户缓存,包括终端用户和CDN等中间代理服务器
私有缓存 只有终端用户的浏览器能缓存数据
强制缓存 在缓存有效期内,直接使用本地缓存,不会向服务器发送请求
协商缓存 在前端请求数据时,向服务器端询问数据是否有更新,如果服务器响应数据更新了,则会再次请求新数据;如果数据未更新,服务器会返回304及新的response header通知浏览器从缓存中读取要请求的数据。
强制缓存与协商缓存:协商缓存可以解决服务器端数据更新时,客户端数据更新不及时的问题。(比如在强制缓存情况下,一开始协商了缓存有效期,万一有效期内服务器端更新了数据,客户端也不会再次请求新数据)。
强缓存在命中时不会发送请求,而协商缓存会发送请求。
缓存实现
缓存字段一般是后端配置的。
实现共享缓存:
设置Cache-Control字段为public。
实现私有缓存:
设置Cache-Control字段为private。
如果响应具有 Authorization
标头,则不能将其存储在私有缓存(或共享缓存,除非 Cache-Control 指定的是 public
)中。
实现强制缓存:
设置Cache-Control的max-age值。
max-age表示在请求正确返回时间的max-age时间内缓存有效。max-age的单位是秒。
或者设置Expires,Expires会指定一个具体的过期时间,缓存在过期时间内有效。但是,由于Expires设置的时间是基于本地电脑的时间,而本地电脑的时间可以更改,数据不太准确,所以目前不太使用。
Expires的优先级比Cache-Control低,同时设置的情况下,Expires不会生效。
实现协商缓存:
设置Cache-Control:no-cache
no-cache:不使用本地缓存,需要使用协商缓存,与服务器确认返回的响应是否有更新,如果之前的响应中有ETag,请求时会与服务器端进行验证,如果资源未更新,则不会重复请求。
想实现协商缓存,首先需要设置no-cache,然后需要Last-Modify/If-Modify-Since/Etag/If-None-Match辅助。
使用Last-Modify和If-Modify-Since判断缓存是否命中:
在前后端初次交互时,如果设置了协商缓存,服务器端除了响应数据,还可以携带Last-Modify字段,Last-Modify是一个时间,记录了请求资源的最后修改时间。当浏览器再次请求资源时,request的header中会携带If-Modify-Since,他是之前返回的Last-Modify值。服务器端收到If-Modify-Since,如果服务器端资源的最后修改时间和If-Modify-Since一致,说明资源没有修改,服务端会返回304;如果不一致,说明资源被修改,会返回200,以及新的资源,和新的最后修改时间。
Last-Modify的精确度是秒,如果资源的修改是毫秒级的,协商缓存无法监测到这种更新,在后端判断本地修改时间是否和If-Modify-Since一致时,会判断一致并返回304,产生BUG。
使用Etag和If-None-Match判断缓存是否命中:
还有一种判断资源是否被修改的方法,是通过Etag和If-None-Match,Etag是需要服务器端生成的一个hash值,这个hash值是资源文件对应的hash值,当前后端初次通信时,服务器端在响应时,会携带这个hash值作为资源在服务器端的唯一标识(Etag)。前端接收到Etag,之后每次向服务器端请求资源时,会带上If-None-Match字段,这个字段的值就是Etag值,后端接收到If-None-Match字段,会把这个字段和服务端目前资源的hash值进行比较,如果一致,则返回命中304。如果不一致,则返回200、新的资源和新的Etag值。
Etag和Last-Modified对比:
Etag的精确度要优于Last-Modified,Last-Modified的精确度是秒级别,而Etag的精确度不受时间限制。
但在性能上,Etag要劣于Last-Modified,因为Etag需要服务器端通过算法生成hash值,而Last-Modified只需要记录时间。(Etag可以通过弱验证优化性能,就是取资源的某部分的hash值判断资源是否更新)
在使用协商缓存时,尽量使用Etag。
设置不进行缓存:
设置Cache-Control:no-store
no-store:直接禁止浏览器缓存数据,每次请求资源时,都会向服务器发送请求,并且每次都会返回完整的资源。
缓存整体流程:
浏览器在发送请求时,会先查找本地是否有缓存,如果有缓存,会判断缓存是否过期,如果缓存未过期,则从缓存读取数据。(先判断是否有强制缓存,如果有且没过期,就用强制缓存,如果强制缓存过期了,或者没有缓存,判断是否设置了协商缓存,如果有协商缓存走协商缓存,如果没有协商缓存,走普通请求)如果已经过期了,会判断这个资源之前请求时是否返回过Etag,如果有Etag,会向服务器请求时携带If-None-Match,由服务器端决策是否命中缓存。如果没有Etag,会判断之前是否返回过Last-Modified,如果有,则在请求时携带If-Modified-Since,同样由服务器端决策是否命中缓存。命中缓存返回304,未命中返回200、新的数据、新的标识字段。如果Etag和Last-Modified都没有,则会向服务器端发送请求。服务器端在响应里配置缓存策略。