1. Overview
我们都知道缓存 ( cache
) 是提高性能的法宝。内存,数据库,网络中缓存机会无处不在,在 Web 应用程序同样也是如此。比如浏览器在打开一个网页的时候,如果需要加载图片,CSS 或者 JavaScript 资源的时候,如果之前浏览器下载过这些资源,就可以跳过 HTTP 请求的部分,这是浏览器私有的缓存。
还有一些缓存是在服务端实现的,将请求的响应缓存起来,在后续的请求如果命中缓存,则直接返回。
这是一个响应缓存的示意图
这里的 Cache
可以指代一个缓存中间件。
2. ResponseChachingOptions
ResponseCahcingOptions
的定义如下
SizeLimit
定义了所有的响应缓存的大小,默认 100 兆MaximumBodySize
单个响应缓存大小,默认 64 兆UseCacheSenesitivePaths
是否请求的路径是大小写敏感,默认不敏感
3. IResponseCahingPolicyProvider
这个接口定义了缓存策略的操作,比如是否允许查询缓存,是否允许保存缓存,缓存的是否有效。
3.1 AttemptResponseCaching
从中我们可以知道,是否允许缓存的条件是
- 只有
Get
和Head
请求才允许缓存查询 - 如果
Header
中包含了Authorization
字段,则不允许缓存查询
3.2 AllowCacheLookup
这里同样也定了另外的缓存查询条件
- 如果请求的
Header
中包含了Cache-Control: No-cache
条件,则跳过缓存查询 - 如果请求中
Header
中 抱恨了Pragma:No-Cache
条件,则跳过缓存查询(注意这是 HTTP/1.0 的协议)
3.3 AllowCacheStorage
如果请求的 Header
中包含 Cache-Control: no-store
则返回的请求不要进行缓存。
3.4 IsResponseCacheable
这里定了若干响应能够被缓存的条件
Response
的Header
只能包含cache-control: public
字段,在HTTP
协议中,Public
意味着响应可以被任何缓存保存;与之相对的是private
,它表明响应只针对单个用户,缓存起来没有很大用处。Response
的Header
包含了cache-control:no-store
字段,表示不能缓存Response
的Header
包含了cache-control:no-cache
字段,表示不能缓存Response
的Header
包含了Set-Cookie
字段,不进行缓存Response
的Header
包含了Vary: *
则不进行缓存处理。Vary
字段表明在将来的请求的时候,哪些请求将来可以使用缓存,比如如下
一开始我们的请求是接受 Content-Encoding: gzip
的请求,在请求返回的response 中包含了 Var:Content-Encoding
这个字段,那么 Cache
的时候也将 br
这个字段也放入到缓存中。
Response
的Header
包含了cache-control:private
, 则不能进行缓存Response
的StatusCode
不是 200, 则不进行缓存操作Response
的ResponseTime
大于ResponseExpires
,则不进行缓存操作Response
的Age
大于maxAge
,则不进行缓存操作- 否则都可以进行缓存
3.5 IsCachedEntryFresh
主要是用来判断返回的请求,是否满足 Fresh
的要求。
4 IResponseCache
IResponseCache
主要缓存的 Response
的读取和写入操作, 操作的对象是 IResponseCacheEntry
对象
主要缓存的对象是响应的上面的字段。
5. IResponseCachingKeyProvider
IResponseCache
负责将缓存的响应放入到缓存中,那么缓存的 Value
我们刚刚讨论过了,那么缓存的 Key
该如何确定呢? IResponseChacingKeyProvider
负责根据请求创建 key
, ResponseCahingKeyProvider
是其中的一个实现,那么它是怎么做到的呢?
BaseKey
的创建的格式如下
GET\x1eSchema\x1eHOST:PORT/PathBase/PATH
StorageVaryKey
的格式如下
BaseKey\xleH\xleHeaderName=HeaderValue\xleQ\xleQueryName=QueryValue1\x1fQueryValue2
LookupVaryKey
[ StroageVaryKey ]
6. Invoke Process
整个过程比较 straightforward
- 构造一个
ResponseCachingContext
对象,该对象在封装了HttpContext
,并且增加了一个属性 - 然后是通过
PolicyProvider
的AttemptResponseCaching
来判断是否可以通过缓存来操作 - 如果是,则返回判断是否允许缓存查找,并且尝试从缓存中获取,如果成功,则理解返回
- 如果没有成功,则判断是否允许缓存操作
- 如果是,调用
ShimResponseStream
方法,来做一些预处理 - 调用
next
方法,继续后续的操作 - 调用
FinalizeCacheBody
方法,将后续 middleware 的得到的 response 缓存起来 - 如果上述的都不满足条件,则直接调用
_next
的中间件。