翻译自 :Cache · App-vNext/Polly Wiki · GitHub
点此跳转到系列
目录https://blog.csdn.net/weixin_41957094/article/details/124854458
Cache (v5.4.0 起可用)
目标
从可用的缓存中获取返回值.
前提: '想当比例的请求是类似请求'
Polly 的CachePolicy
是通读缓存模式的一种实现, 这种模式也叫做 cache-aside pattern. 通过从缓存中获取结果来减少总体的调用耗时和网络流量.
从内存缓存中检索结果可以完全消除下游调用. 分布式缓存可用用于上游节点之间共享缓存; 也可以用于从相邻网络中获取资源而不是从下游获取(PS:例如CDN静态文件缓存); 也可用用于应用程序内存缓存不足的替代.
使用缓存供应器(CacheProviders)
Polly 的 CachePolicy
需要搭配缓存供应器(ISyncCacheProvider
或 IAsyncCacheProvider
的实现).
下方列表列取列取了一些已实现的缓存供应器,可获取对应的Nuget包使用:
Package | Description | Supported targets |
---|---|---|
Polly.Caching.Memory (nuget for Polly >=6.0.1 ; nuget for Polly <=5.9.0 ; github and doco) | 一种基于 .NET Framework / .NET Core 实现的本地内存缓存供应器. | .NET 4.0 .NET 4.5 .NET Standard 1.1 (supports .NET Core and Xamarin) .NET Standard 2.0 |
Polly.Caching.Distributed (nuget ; github and doco) | 支持基于.NET Core的所有分布式缓存 IDistributedCache, 支持Redis和SqlServer缓存供应器. | .NET Standard 1.1 (supports .NET Core and Xamarin) .NET Standard 2.0 |
Use the below compatibility grid to select the correct version of cache providers to use with your version of Polly:
Version of Polly | Version of above cache providers to use |
---|---|
V6及之前版本 | v2 |
v7 起 | v3 起 |
使用
- 缓存的Key取决于所使用的Key策略.
- 对应的缓存Key中有对应的缓存值时,执行顺序:
- 传递给
.Execute(...)
或类似的委托不被调用执行 - 从缓冲中获取返回值.
- 传递给
- 对应的缓存Key中没有对应的缓存值时,执行顺序:
- 传递给
.Execute(...)
或类似的委托正常调用执行 - 使用配置的生存时间策略将检索到的值放入缓存中
- 返回检索到的值.
- 传递给
语法
CachePolicy 的定义
CachePolicy cache = Policy .Cache(ISyncCacheProvider cacheProvider , TimeSpan ttl | ITtlStrategy ttlStrategy [, ICacheKeyStrategy cacheKeyStrategy | Func<Context, string> cacheKeyStrategy] [, Action<Context, string, Exception> onCacheError] | [, Action<Context, string> onCacheGet , Action<Context, string> onCacheMiss , Action<Context, string> onCachePut , Action<Context, string, Exception> onCacheGetError , Action<Context, string, Exception> onCachePutError] ); CachePolicy cache = Policy .CacheAsync(IAsyncCacheProvider cacheProvider , TimeSpan ttl | ITtlStrategy ttlStrategy [, ICacheKeyStrategy cacheKeyStrategy | Func<Context, string> cacheKeyStrategy] [, Action<Context, string, Exception> onCacheError] | [, Action<Context, string> onCacheGet , Action<Context, string> onCacheMiss , Action<Context, string> onCachePut , Action<Context, string, Exception> onCacheGetError , Action<Context, string, Exception> onCachePutError] ); CachePolicy<TResult> cache = Policy .Cache<TResult>(ISyncCacheProvider<TResult> cacheProvider, /* etc */); CachePolicy<TResult> cache = Policy .CacheAsync<TResult>(IAsyncCacheProvider<TResult> cacheProvider, /* etc */);
缓存策略的使用
就像其他Polly策略一样, 一个缓存策略也可以被多处功能点代码复用. 通过将Context
实例传递给委托执行的代码,可以为特定执行指定缓存键:Context
实例上的OperationKey
是将缓存的键。
TResult result = await cachePolicy.ExecuteAsync(context => getFoo(), new Context("FooKey"));
上面的模式假设你正在使用默认缓存键策略。
你也可以在配置策略是指定 自定义的缓存键策略 .
参数
缓存供应器
cacheProvider
: 提供所需要的缓存.
Polly 的 CachePolicy
需要搭配缓存供应器(ISyncCacheProvider
或 IAsyncCacheProvider
的实现).: 已实现的缓存供应器可以参考上表,或者你自定义缓存供应器
序列化器(见下面)也可以与缓存提供程序一起使用,将执行的“TResult”类型序列化为供应器所需的“TCache”类型。
相同的缓存供应器和序列化器实例也可以在多处复用。
ttl
TimeSpan ttl
: 缓存项的生存时间(Time-to-live, ttl),即从条目放入缓存的那一刻起的相对的、非滑动的持续时间。
例如,如果传入' TimeSpan.FromMinutes(5) ', ' cacheProvider '应该认为该条目在5分钟内有效。
ttl策略 (上述TTL的替代方案)
ITtlStrategy ttlStrategy
: 除了上面简单的“TimeSpan ttl”,它还提供了一些ttl策略。
RelativeTtl
RelativeTtl(TimeSpan ttl)
: 等价于上面的 ttl
.
AbsoluteTtl
AbsoluteTtl(DateTimeOffset absoluteExpirationTime)
: 指示cacheProvider
应使缓存项在给定的绝对时间过期。
SlidingTtl
SlidingTtl(TimeSpan slidingTtl)
: 指示cacheProvider
应将缓存项视为具有指定时间跨度的滑动生存时间。 例如,如果传入TimeSpan.FromMinutes(5)
, 即每次缓存项被命中时cacheProvider
应该在接下来的五分钟认为该缓存项仍然有效。
ContextualTtl
ContextualTtl
: 表名执行应该从传递给执行的Context
参数中的context[ContextualTtl.TimeSpanKey]
获取ttl
。
这允许你定义一个中央缓存策略,通过将期望的ttl放在Polly的执行上下文中,在不同的调用地点使用不同的ttl。例如:
context[ContextualTtl.TimeSpanKey] = TimeSpan.FromMinutes(5);
context[ContextualTtl.SlidingExpirationKey] = true; // if desired; if not set, false is assumed
ResultTtl
ResultTtl
: 指定一个函数,该函数将用于根据缓存的ResultTtl
项计算ttl
。这在任何需要结果表明(可能通过头)应该缓存多长时间的场景中都是有用的。 例如,调用获得授权令牌,调用结果还告诉您该令牌的有效时间。
ResultTtl(Func<TResult, Ttl>)
: 指定一个基于缓存的“TResult”项计算Ttl的函数.
ResultTtl(Func<Context, TResult, Ttl>)
: 指定一个函数,根据缓存的“TResult”项和执行的“Context”计算缓存的生存时间。
默认缓存键策略
如果不指定 cacheKeyStrategy
, 缓存的键就是用代码执行Context
的 OperationKey
(context.OperationKey
). 例如:
TResult result = await cache.ExecuteAsync(async context => await getFooAsync(), new Context("FooKey")); // "FooKey" is the cache key to use in this execution.
如果context.OperationKey
不指定 (不给执行传入Context
, 或者不设置context.OperationKey
), 则不缓存结果值, 传递给.Execute(...)
(或类似) 的委托调用正常执行.
v6及之前的版本, OperationKey
被命名为ExecutionKey
.
自定义缓存键策略
Func<Context, string> cacheKeyStrategy
:允许使用自定义策略,该策略在执行中使用更加具体的缓存键。 例如,将执行中的guid与缓存Key结合起来: // configuration CachePolicy cache = Policy.CacheAsync(cacheProvider, TimeSpan.FromMinutes(5), context => context.OperationKey + context["guid"]);
// usage, elsewhere
Guid guid = ... // from somewhere
Context policyExecutionContext = new Context("GetResource-");
policyExecutionContext["guid"] = guid.ToString();
TResult result = await cache.ExecuteAsync(async context => await getResourceAsync(guid), policyExecutionContext); // "Resource-SomeGuid" is the key used in this execution, if guid == SomeGuid.
ICacheKeyStrategy cacheKeyStrategy
: 在一些重载中作为参数,用于更复杂的函数。
使用序列化器
一些缓存提供商(如Redis)以特定类型存储缓存项(如string
或byte[]
),要求你序列化更复杂的类型到那些(string
or byte[]
)。
以下已存在的序列化器可用于Polly策略:
Package | Description |
---|---|
Polly.Caching.Serialization.Json (nuget ; github and doco) | 一个基于Newtonsoft.Json实现的可序列化任何类型为String的序列化器(NuGet Gallery | Newtonsoft.Json 13.0.1). |
Polly 缓存策略的序列化器的使用注意事项请参阅这里 . 实现新的序列化器也很容易(实现缓存序列化器).
与策略操作交互
onCacheGet
可选的onCacheGet
委托允许在从缓存中检索值时执行特定的代码(例如日志记录)。
onCacheMiss
一个可选的onCacheMiss
委托允许特定的代码执行(例如日志),当无法命中缓存时执行(一个值在缓存中找不到给定的Key)。
onCachePut
可选的onCachePut
委托允许在值被放入缓存后执行特定的代码(例如日志记录)。
onCacheError
如果对底层的cacheProvider
的任何调用引发异常,可选的onCacheError
委托允许执行特定的代码(例如日志记录)。 如果配置了onCacheError
委托,它将同时用于onCacheGetError
和onCachePutError
。
onCacheGetError
另一个可选的onCacheGetError
委托是onCacheError
的更特定版本,只有在调用底层的cacheProvider
get缓存抛出异常时才会执行。
onCachePutError
另一个可选的onCachePutError
委托是onCacheError
的更具体版本,只有当对底层的cacheProvider
的put调用抛出异常时才会执行。
所有以上委托的输入参数
以上所有委托都接受执行的“Context”和缓存键“string”作为输入参数。 错误捕获委托也接受由缓存提供程序抛出的“Exception”。
抛出异常
不会因为缓存操作而引起系统异常。 如果底层的cacheProvider
在缓存操作期间抛出异常:
- 如果配置了捕获缓存的委托,则异常会传递给相关的
onCacheError
,onCacheGetError
或onCachePutError
委托。 - 继续执行. 例如,如果底层的' cacheProvider '在检查缓存是否包含给定键的值时抛出,则执行将其视为cache miss,并调用传递给' . execute(…)'的委托。
- 也就是说, 在将异常传递给
onCacheError
,onCacheGetError
或onCachePutError
用于(比如)日志记录之后,执行有意地吞下该异常。这样,缓存本身就永远不会使应用程序宕机。
- 也就是说, 在将异常传递给
推荐用法
在“PolicyWrap”内放置缓存策略
参见策略组合使用指导. :CachePolicy
通常应该放在PolicyWrap
的最外面,放在FallbackPolicy
的外面。
没返回值的执行
如果缓存策略所执行的业务代码没有返回值, 缓存操作自然就跳过 而不会抛出异常 (因为本身就没有结果可缓存). 这允许CachePolicy
包含在PolicyWrap
中,PolicyWrap
有时用于返回treresult
的执行,有时用于返回void
,而不会抛出异常。
只选择性地缓存响应
在某些情况下,您可能只想缓存执行的某些响应,而不是其他响应。 一个典型的例子是当CachePolicy管理的代码返回'HttpResponseMessage'时,你只想在HttpResponseMessage
StatusCode = = HttpStatusCode.OK 时进行缓存。
这可以通过使用 ResultTtl策略来实现,该策略通过返回值中的'Ttl'来判断是否需要缓存。TimeSpan.Zero
标识不需要缓存:
Func<Context, HttpResponseMessage, Ttl> cacheOnly200OKfilter =
(context, result) => new Ttl(
timeSpan: result.StatusCode == HttpStatusCode.OK ? TimeSpan.FromMinutes(5) : TimeSpan.Zero,
slidingExpiration: true
);
IAsyncPolicy<HttpResponseMessage> cacheOnly200OKpolicy =
Policy.CacheAsync<HttpResponseMessage>(
cacheProvider: /* the cache provider you are using */,
ttlStrategy: new ResultTtl(cacheOnly200OKfilter),
onCacheError: /* whatever cache error logging */
); // (or other richer CacheAsync overload taking an ITtlStrategy ttlStrategy)
当ITtlStrategy
返回TimeSpan.Zero
, 策略跳过将返回值放到缓存中这一步骤.
_注意 在根据HttpResponseMessage
的级别缓存时需要注意一些问题: 参见讨论: Is caching at the HttpResponseMessage level the right fit?.
为值类型缓存' default(TResult) '
在Polly v6版本, 缓存策略不缓存“default(TResult)”的结果值, 类似于.NET框架的原始约定,缓存返回null意味着在缓存中找不到该键的值. 一个不不好的影响是,这会阻止缓存值类型的default(TResult)
(对于default(TResult) != null
) 。在 Polly的 v6版本, 为了确保您可以为值类型缓存default(TResult)
,请将执行类型更改为TResult?
。
从Polly v7.0.0(使用缓存提供程序>=v3.0.0)开始,缓存策略允许对所有值和引用类型缓存“null”和“default(TResult)”。
线程安全和策略复用
线程安全
CachePolicy
的内部操作是线程安全的:一个实例上的多个调用时线程安全的 (假设配置的cacheProvider
实现也是线程安全的).
策略复用
CachePolicy
实例可以在多个调用点复用.
cacheProvider
实例可以在多个`CachePolicy和调用点上重用。
序列化器实例可以跨多个' CachePolicy '和调用点重用。
在重用策略时,使用不同的' OperationKey '来指定缓存键(如果使用了' DefaultCacheKeyStrategy '),并在日志和度量中区分不同的调用站点用法。
实现缓存提供程序和序列化程序
缓存提供程序
参见 实现新的缓存提供程序
创建和使用序列化器
参见 Cache · App-vNext/Polly Wiki · GitHub
翻译自 :Cache · App-vNext/Polly Wiki · GitHub