当前位置: 首页 > 科技观察

图解HTTP缓存机制-实用HTTP

时间:2023-03-22 01:05:20 科技观察

1.前言大家好,我是呈祥墨影!HTTP协议在网络知识中占有重要地位。HTTP协议最基本的就是请求和响应的头部(Header)。HTTP协议的大部分使用依赖于设置不同的HTTP请求/响应标头。实现了。本系列《实用 HTTP》抛开常规的Header解释表达式,从实际问题出发,分析这些Http协议的使用。他们试图解决什么问题?同时解释它是如何设计的以及它是如何工作的。HTTP协议是一种无状态的“松散协议”,它不记录不同请求的状态,并且由于它本身包含两端(客户端和服务器),根据请求和响应来区分,它的大部分内容只是一个这个建议,其实双方都不能遵守这个建议。比如:服务端说这个数据缓存时效一天,但是客户端可以说,我不听,我不听,每次都得重新请求。“这里建议零售价2元……”“哦,我不接受这个建议!”说到缓存,本文就讲一下HTTP缓存。2.HTTP缓存的使用2.1为什么需要缓存?说白了,缓存就是为了速度。无论是从磁盘到内存,还是从网络到本地,都是为了快速响应,避免下次使用这个资源时的多次I/O。操作。通过网络获取资源是一个耗时的操作。更大的资源会需要客户端和服务器进行多次往返通信,这不仅会增加客户端的响应时间,还会增加网络流量。在HTTP协议中,天然就支持缓存。在浏览器和应用程序使用的开源网络库中,使用HTTP缓存来缓存资源。浏览器天生就支持HTTP缓存,而开源库则需要一些简单的设置,比如存储规则、缓存资源存储路径等。2.2设计缓存策略如果我们要设计缓存策略,首先要考虑两个重要的指标。1、缓存失效由于缓存主要是为了数据复用,所以我们需要一个条件来判断当前缓存的数据是否仍然有效。不能缓存一次,但可以终身使用。我们还需要在缓存过期后检索新数据并缓存它。前提是缓存需要有失效策略。2.减少阅读。虽然缓存有失效策略,但它只是被客户端认为是无效的。这时候应该再去服务器端取数据。但是,在某些情况下,资源可能仍然有效且未更改。那么就需要有一个策略让服务端通知客户端当前缓存仍然有效,可以继续使用。这样一来,除了可以减少传输流量,还可以加快响应时间,提高效率。这是必须考虑好的缓存策略的地方。其实HTTP缓存也是这么设计的。2.3HTTP缓存HTTP缓存主要是通过请求和响应头中对应的Header信息来控制缓存策略。这里主要涉及两个header:Cache-Control:设置缓存策略,是否使用缓存,超时时间。ETag:当前返回数据的验证token,可以是Hash值,也可以是其他指纹。主要是用来在下次请求时携带,让服务器根据这个判断当前数据是否发生变化。服务器在返回响应数据时,会在头部添加内容类型、数据长度、缓存策略(Cache-Control)、验证令牌(ETag)等信息来描述当前响应。例如,上图显示了一个请求-响应事务。客户端请求文件时,服务器返回状态码200,表示响应正常。响应数据的长度为1024字节。建议客户端发送此资源最多缓存120秒,并提供指纹令牌(“cxmyDev123”)以唯一标识当前数据。2.4ETag数据令牌Cache-Control中设置的max-age很容易理解。就是设置缓存超时时间。HTTP缓存是通过限制超时秒数来确定缓存过期的时间。古代也用expires来判断超时日期,现在已经废弃了。如果与Cache-Control同时存在,则以Cache-Control为准。在此时间间隔内,客户端不会向服务器发送新的请求。当资源与上次缓存的时间间隔大于120秒时,客户端会再次向服务器发送请求。如果没有datatoken,一般的步骤应该是这样的:1.客户端会先找到本地缓存,然后发现已经过期,不能再使用了。2、客户端再次向服务器发送新的请求,再次获取完整的数据进行缓存。之后刷新缓存的超时时间。但这是一件效率很低的事情。服务器无法确定自己持有的源资源什么时候会失效,所以提供的max-age值只是一个参考值,需要进行平衡。太短会导致频繁请求,太长会导致无法及时刷新客户端资源。这时候再次请求时,有一定概率客户端缓存的数据和服务端持有的数据是一致的,所以我们不需要再次缓存这个数据资源,直接使用数据resource之前client缓存的数据足够了,需要刷新缓存超时时间。这正是数据验证令牌(ETag)要解决的问题。服务器生成并返回的数据指纹token,通常是返回数据的Hash值或其他数据指纹。客户端不需要关心它的生成规则,只需要知道它是当前数据的唯一标识即可。客户端需要在下一个请求中通过If-None-Match请求头将验证令牌发送给服务器。如果数据令牌的指纹与服务器上的当前数据一致,则表示该资源尚未更新。改变。会返回304状态码,表示客户端本地缓存中的数据可以继续使用,超时时间会刷新。注意当响应码为304时,不包含数据内容。通常这个缓存操作对我们来说是透明的。它是浏览器和开源网络库的基本实现。max-age和ETag的值我们不需要自己去判断在这一步中,我们只需要确保服务器支持即可。.这里只提到If-None-Match,用来标识ETag是否不一致。除此之外,还有一些其他相关的头文件,比如If-Match。有兴趣的可以参考相关资料。2.5Cache-Control在前面的例子中,我们只是给Cache-Control设置了一个max-age,其实还有一些更丰富的配置。从缓存性能优化的角度来看,最好的缓存是不需要和服务器通信的缓存,可以通过缓存消除网络延迟和数据请求,从而提升用户体验。Cache-Control是在HTTP/1.1中定义的,它可以用来替代以前的缓存策略,现在所有的浏览器都支持Cache-Control,它已经成为了一个通用的标准。Cache-Control还有一些更灵活的配置,可以对缓存进行更详细的操作。1、“no-cache”和“no-store”这两个参数都表示每次请求需要实际发送一次网络请求。它们的区别在于“无缓存”并不是真的不缓存数据,只是每次都需要确认资源是否过期,即会使用数据令牌ETag来一定程度上减少传输流量。而“no-store”完全需要客户端每次都重新请求数据,下载最新的数据,不做任何缓存。这种非缓存策略还包括proxy、gateway等中间连接等中间传输通道,完全不缓存数据,每次都从源服务器获取数据。2.“public”和“private”“public”是默认策略,表示当前缓存是开启的,可以缓存请求响应中的任何中间环节。如果我们不明确指定,则当前是“公共”缓存。相比之下,“private”意味着当前响应是针对单个用户的,而不是一般数据,因此不建议任何中间缓存对其进行缓存。例如:浏览器是一个比较私有的缓存源,它会缓存“私有”的缓存,但是CDN不会。3、最新的缓存策略树前面说了,缓存的核心目的就是要快,以便下次使用的时候能够快速复用。所以理想情况下,我们应该尽可能缓存尽可能多的响应数据,并尽可能长时间地缓存,并为每个资源提供一个单独的数据验证令牌,以便在时间到期后快速验证。但一切都必须平衡。没有最优的缓存策略。并不是所有的响应资源都需要缓存,需要根据业务场景进行设置。这里有一个增加HTTP缓存的通用策略树,你可以在给响应添加缓存的时候参考执行。一般情况下,我们会针对不同的响应属性设置不同的缓存策略。下面是一些基于场景的示例。3.1对于用户相关的数据和与单个用户密切相关的数据,我们通常不建议使用缓存,但还是有几个层次。1.严格不要使用缓存Cache-Control:no-store2。允许客户端缓存,但每次使用Cache-Control需要确认:no-cacheETag:"cxmyDev1234"3.允许客户端短时间缓存Cache-Control:privatemax-age=600ETag:"cxmyDev1234"3.2通用数据一些通用的响应资源,更新频率很低,我们可以根据需要调整max-age的大小。Cache-Control:max-age=86400ETag:"cxmyDev1234"4.一旦丢弃和更新缓存响应缓存的策略被确定并传递给客户端,服务器就失去了对对齐的控制。也就是说,如果我们设置了max-age,在这个资源的有效期到期之前,即使服务器上的源资源已经被更换和修改,我们也没有合适的时间通知客户端更新新的响应数据。那么有没有什么好的策略来标记资源过时呢?同时,它可以很好地利用缓存策略。在互联网上,所有服务上的资源都有一个对应的URL(UniformResourceLocator),可以清楚地说明如何从一个精确固定的位置获取资源。HTTP缓存也依赖于URL。请注意,URL区分大小写。相同的URL代表相同的请求响应。以此为基础,可以判断缓存和后续缓存的复用情况。所以我们可以在URL上做文章。4.1浏览器的放弃策略如前所述,浏览器天生就支持HTTP缓存。对于浏览器来说,他们面对的是HTML页面,其中包含一些CSS、Image、JavaScript、JSON资源和数据。对于不同的资源和数据,我们可以在它们的url中添加datatokenfingerprints,当资源发生变化时,我们也可以同时刷新和更改fingerprinttoken。这里很好理解:HTML页面,使用no-cache,每次都强制向源服务器确认数据。CSS文件通常很少更改,所以可以在中间层允许缓存,缓存时间为一年,不会过期。JavaScript中有业务逻辑,可以设置为只允许客户端缓存。getUserInfo与个人用户数据有关。这里建议可以缓存,但是每次都需要向服务器重新确认。4.2App界面缓存策略App中使用的界面其实和网页是不一样的。HTML网页的结构类似于树结构。首先通过获取.html文件获取其中所有资源的表格,然后根据缓存策略进行处理。使用权。而在App中,与服务器的交互是通过数据接口实现的。一开始就没有像HTML文件那样的树状界面。每个接口都是一个“孤岛”,可以独立存在。我们无法提前知道某个接口的响应数据已经过期,也无法修改URL上携带的数据指纹token。但实际上,我们可以将App和设备的一些固有信息作为URL参数传递给刷新数据。比如这里/app/main获取的是首页的数据。这里将当前app的版本号作为参数拼接在url后面,实现强制不同版本,刷新不同数据。避免使用刚刚升级且仍在使用旧版本数据的应用程序。在这个例子中,版本号只是维度之一。如果需要,还可以传递其他维度的信息,比如当前网络状态、当前用户id等。五、小结至此我们基本上把HTTP缓存的相关内容都说完了,这里简单总结一下。HTTP缓存依赖URL进行唯一标识,不同的URL使用不同的缓存。Cache-Control可以控制缓存策略,公有还是私有,缓存超时时间等。使用ETag标记数据指纹token来判断响应数据是否更新。应该为每个响应资源提供相应的缓存策略。如果需要丢弃之前的缓存,可以通过修改请求URL,在URL后面追加数据指纹token来更新数据。关于HTTP缓存,大家有什么更好的想法可以在留言区讨论。参考资料:《HTTP缓存》:http://t.cn/RL1NI8P《基于缓存策略三要素分解法》【本文为专栏作者“张扬”原创稿件,转载请微信联系作者公众号获得授权】点此查看作者更多好文章