谈一谈Etag这个功能

Etags或者Entity tags是一种后端常用的server loading reduction的一种机制。

功能目的:
1. Caching
○ 管理一个resource的cache lifecycle。
○ 管理什么时候需要更新一个缓存。
2. Conditional requests
○ 通过一个validator,去验证一个resource是否被影响。

什么是Conditional Header?什么是Validator?

Conditional headers是用来验证一个服务器的resource是否匹配一个特定的版本,一般会出现在Http Request里面。

常用的Conditional Header:
○ If-Match
○ If-None-Match
○ If-Modified-Since
○ If-Unmodified-Since

Validator则是一个值存在于Conditional header里面,被Server用于匹配特定的版本。

后端开发的时候,通常不会一个byte一个byte的去比较两个版本(Strong Validation),这样非常消耗时间和资源。为了节省成本,我们会使用一个值去进行快速比较(Weak Validation),这个值就是我们所谓的Validator。

Validator有两种:
• Last-modified date - 上次修改的日期.
• An opaque string又或者叫entity tag或etag。

详细的信息可以从Mozilla的官方文档里了解

https://developer.mozilla.org/en-US/docs/Web/HTTP/Conditional_requests

使用场景:
Etag会在以下两种情况下使用。

  1. 服务器回复的response header里面会包含body的Etag value。
  2. 客户端发送的request经常会使用一些control request header(比如If-Match和If-None-Match)来管理Etag value。

流程图:

具体流程示范:
第一步,客户端发送一个rest api请求,服务器回复的response里面会带有一个etag header。

第二步,在之后的客户端request里面会包含一个if-none-match的request header,这个if-none-match的值是之前response的etag value。当服务器端收到请求后,将会比对目前resource的hash value是不是和新接受的request的etag value一样。一旦两个数值相等,服务器回复一个status code 304 Not Modified和比较的etag value。

第三步,为了测试Etag不相等的场景,我们先发送一个put request改变当前resource的值。

如果下次请求还是使用第一次的etag值“f88dd058fe004909615a64f01be66a7”,服务器会送回一个包含message body的一个response,因为服务器比较当前的resource的etag和服务器request的etag不相等。

计算方式:
Etag的值可以想成一个response body的hash value或者checksum,对于service来说,一点点body上面的改动,最后的hash value会有很大的变化。

Common methods of ETag generation include using a collision-resistant hash function of the resource’s content, a hash of the last modification timestamp, or even just a revision number.
-Wikipedia

推荐算法:md5, adler32, sha256等哈希算法。
(需要考虑使用场景,正常情况下尽量使用collision-resistant的哈希算法,当然如果选择哈希算法速度太慢,也可以考虑用更简单的算法)

CRC32或者CRC64因为collision太多,不太适合用于Etag generation。

Spring Framework的shallowEtag使用的是Md5。

如何在Spring里用快速使用Etag这个功能呢?
非常简单,在web.xml里面增加一个filter。注意这个方式是Shallow Etag。

然后我们在spring context里面declare一个ShallowEtagHeaderFilter bean.

如果需要更多的配置,我们可以创建一个FilterRegistrationBean的一个实例,用来设置更多属性。

什么是ShallowEtag:
单纯的使用response去计算etag。

这个方式可以有效的节省bandwidth,然而server performance还是跟原来一样。

为什么这么说呢?Server performance在这里指的是总的request和response的数量,如果单纯的使用ShallowEtag是无法减少总的request和response的数量,从我们的流程图来看,不管有没有etag,request和response都是固定数量。如果想提高Server performance的话,我们可以混合long-polling等机制去达成这个目的。

那Bandwidth是怎么减少的呢?使用了Etag之后,Response里面传输的数据量会减少了,因为在Etag值相等情况,后端知道前段不需要更新,所以在Response里面就省略了最新的数据,从而节省了很多Bandwidth。

Spring Framework Etag源代码:
直接对response进行MD5哈希运算,得出Etag后先放入Response header里面。
然后比较request里面IF_NONE_MATCH的Etag值,看其是不是相等。

相关链接:

Reference:






2赞

点赞