![polly](https://i-blog.csdnimg.cn/blog_migrate/d1969ac9cb6633de8571c070a4ed2b9c.png)
polly
In my last blog post Adding Cross-Cutting Memory Caching to an HttpClientFactory in ASP.NET Core with Polly I actually failed to complete my mission. I talked to a few people (thanks Dylan and Damian and friends) and I think my initial goal may have been wrong.
在我的上一篇博文中,使用Polly将交叉剪切内存缓存添加到ASP.NET Core中的HttpClientFactory中,我实际上未能完成任务。 我与一些人交谈(感谢Dylan,Damian和朋友),我认为最初的目标可能是错误的。
I thought I wanted "magic add this policy and get free caching" for HttpClients that come out of the new .NET Core 2.1 HttpClientFactory, but first, nothing is free, and second, everything (in computer science) is layered. Am I caching the right thing at the right layer?
我以为是要为来自新.NET Core 2.1 HttpClientFactory的HttpClient“神奇地添加此策略并获得免费的缓存”,但是首先,没有什么是免费的,其次,一切(在计算机科学中)都是分层的。 我是否在正确的层缓存正确的东西?
The good thing that come out of explorations and discussions like this is Better Software. Given that I'm running Previews/Daily Builds of both .NET Core 2.1 (in preview as of the time of this writing) and Polly (always under active development) I realize I'm on some kind of cutting edge. The bad news (and it's not really bad) is that everything I want to do is possible it's just not always easy. For example, a lot of "hooking up" happens when one make a C# Extension Method and adds in into the ASP.NET Middleware Pipeline with "services.AddSomeStuffThatIsTediousButUseful()."
像这样的探索和讨论中产生的好处是更好的软件。 鉴于我正在运行.NET Core 2.1(在撰写本文时为预览版)和Polly(始终处于积极开发中)的Preview / Daily Builds,我意识到我处于某种前沿。 坏消息(这并不很糟糕)是我想做的所有事情都是可能的,但这并不总是那么容易。 例如,当人们做出一种C#扩展方法,并使用“ services.AddSomeStuffThatIsTediousButUseful()将其添加到ASP.NET中间件管道中时,就会发生很多“钩住”的情况。
Polly and ASP.NET Core are insanely configurable, but I'm personally interested in the 80% or even the 90% case. The 10% will definitely require you/me to learn more about the internals of the system, while the 90% will ideally be abstracted away from the average developer (me).
Polly和ASP.NET Core可以疯狂配置,但是我个人对80%甚至90%的情况感兴趣。 10%的人肯定需要您/我来了解有关系统内部的更多信息,而90%的人最好是从普通开发人员(我)那里抽象出来。
I've had a Skype with Dylan from Polly and he's been updating the excellent Polly docs as we walk around how caching should work in an HttpClientFactory world. Fantastic stuff, go read it. I'll steal some here:
我与Polly的Dylan有了Skype,在我们探讨HttpClientFactory世界中缓存的工作方式时,他一直在更新出色的Polly文档。 很棒的东西,去读吧。 我会在这里偷一些:
ASPNET Core 2.1-什么是HttpClient工厂? (ASPNET Core 2.1 - What is HttpClient factory?)
From ASPNET Core 2.1, Polly integrates with IHttpClientFactory. HttpClient factory is a factory that simplifies the management and usage of
HttpClient
in four ways. It:从ASPNET Core 2.1开始,Polly与IHttpClientFactory集成。 HttpClient工厂是从四个方面简化
HttpClient
管理和使用的工厂。 它:
allows you to name and configure logical
HttpClient
s. For instance, you may configure a client that is pre-configured to access the github API;允许您命名和配置逻辑
HttpClient
。 例如,您可以配置一个预先配置为访问github API的客户端。manages the lifetime of
HttpClientMessageHandler
s to avoid some of the pitfalls associated with managingHttpClient
yourself (the dont-dispose-it-too-often but also dont-use-only-a-singleton aspects);管理
HttpClientMessageHandler
的生存期,以避免与自己管理HttpClient
相关联的一些陷阱(不要经常处置,也不要只使用单个方面);provides configurable logging (via
ILogger
) for all requests and responses performed by clients created with the factory;提供由工厂创建的客户端执行的所有请求和响应的可配置日志记录(通过
ILogger
);provides a simple API for adding middleware to outgoing calls, be that for logging, authorisation, service discovery, or resilience with Polly.
提供了一个简单的API,用于将中间件添加到呼出电话中,用于记录,授权,服务发现或与Polly的弹性。
The Microsoft early announcement speaks more to these topics, and Steve Gordon's pair of blog posts (1; 2) are also an excellent read for deeper background and some great worked examples.
在微软早期公布说更多的这些话题,和史蒂夫·戈登的对博客文章( 1 ; 2 )也是更深层次的背景和一些伟大的工作的例子极好的读取。
Polly and Polly policies work great with ASP.NET Core 2.1 and integrated nicely. I'm sure it will integrate even more conveniently with a few smart Extension Methods to abstract away the hard parts so we can fall into the "pit of success."
Polly和Polly策略与ASP.NET Core 2.1配合使用非常好,并且可以很好地集成。 我敢肯定,它将与某些智能扩展方法更加便捷地集成在一起,以抽象出困难的部分,从而使我们陷入“成功的陷阱”。
使用Polly和HttpClient进行缓存 (Caching with Polly and HttpClient)
Here's where it gets interesting. To me. Or, you, I suppose, Dear Reader, if you made it this far into a blog post (and sentence) with too many commas.
这就是有趣的地方。 对我来说。 或者,我想,亲爱的读者,如果您到博客文章(和句子)中的逗号太多了,请问。
This is a salient and important point:
这是一个重要的重点:
Polly is generic (not tied to Http requests)
Polly是通用的(与Http请求无关)
Now, this is where I got in trouble:
现在,这是我遇到麻烦的地方:
Caching with Polly CachePolicy in a DelegatingHandler caches at the
HttpResponseMessage
level在DelegatingHandler中使用Polly CachePolicy进行缓存的
HttpResponseMessage
级别的缓存
I ended up caching an HttpResponseMessage...but it has a "stream" inside it at HttpResponseMessage.Content. It's meant to be read once. Not cached. I wasn't caching a string, or some JSON, or some deserialized JSON objects, I ended up caching what's (effectively) an ephemeral one-time object and then de-serializing it every time. I mean, it's cached, but why am I paying the deserialization cost on every Page View?
我最终缓存了一个HttpResponseMessage ...,但是在HttpResponseMessage.Content里面有一个“流”。 只能阅读一次。 未缓存。 我不是在缓存字符串,一些JSON或一些反序列化的JSON对象,而是最终缓存了(有效地)一个短暂的一次性对象,然后每次对其进行反序列化。 我的意思是,它已被缓存,但是为什么我要为每个页面视图支付反序列化费用?
The Programmer's Hindsight: This is such a classic programming/coding experience. Yesterday this was opaque and confusing. I didn't understand what was happening or why it was happening. Today - with The Programmer's Hindsight - I know exactly where I went wrong and why. Like, how did I ever think this was gonna work? ;)
程序员的后见之明:这是一种经典的编程/编码经验。 昨天,这是不透明和令人困惑的。 我不知道发生了什么或为什么发生。 今天-通过程序员的后见之明-我确切地知道我哪里出了问题以及原因。 就像,怎么我曾经认为这是要去工作? ;)
As Dylan from Polly so wisely points out:
正如Polly的Dylan明智地指出的那样:
It may be more appropriate to cache at a level higher-up. For example, cache the results of stream-reading and deserializing to the local type your app uses. Which, ironically, I was already doing in my original code. It just felt heavy. Too much caching and too little business. I am trying to refactor it away and make it more generic!
在更高级别上进行缓存可能更合适。 例如,将流读取和反序列化的结果缓存到应用程序使用的本地类型。 具有讽刺意味的是,我已经在使用原始代码了。 感觉很沉重。 太多的缓存,太少的业务。 我正在尝试将其重构,使其更通用!
This is my "ShowDatabase" (just a JSON file) that wraps my httpClient
这是包装我的httpClient的“ ShowDatabase”(只是一个JSON文件)
public class ShowDatabase : IShowDatabase
{
private readonly IMemoryCache _cache;
private readonly ILogger _logger;
private SimpleCastClient _client;
public ShowDatabase(IMemoryCache memoryCache,
ILogger<ShowDatabase> logger,
SimpleCastClient client)
{
_client = client;
_logger = logger;
_cache = memoryCache;
}
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
public async Task<List<Show>> GetShows()
{
Func<Show, bool> whereClause = c => c.PublishedAt < DateTime.UtcNow;
var cacheKey = "showsList";
List<Show> shows = null;
//CHECK and BAIL - optimistic
if (_cache.TryGetValue(cacheKey, out shows))
{
_logger.LogDebug($"Cache HIT: Found {cacheKey}");
return shows.Where(whereClause).ToList();
}
await semaphoreSlim.WaitAsync();
try
{
//RARE BUT NEEDED DOUBLE PARANOID CHECK - pessimistic
if (_cache.TryGetValue(cacheKey, out shows))
{
_logger.LogDebug($"Amazing Speed Cache HIT: Found {cacheKey}");
return shows.Where(whereClause).ToList();
}
_logger.LogWarning($"Cache MISS: Loading new shows");
shows = await _client.GetShows();
_logger.LogWarning($"Cache MISS: Loaded {shows.Count} shows");
_logger.LogWarning($"Cache MISS: Loaded {shows.Where(whereClause).ToList().Count} PUBLISHED shows");
var cacheExpirationOptions = new MemoryCacheEntryOptions();
cacheExpirationOptions.AbsoluteExpiration = DateTime.Now.AddHours(4);
cacheExpirationOptions.Priority = CacheItemPriority.Normal;
_cache.Set(cacheKey, shows, cacheExpirationOptions);
return shows.Where(whereClause).ToList(); ;
}
catch (Exception e)
{
_logger.LogCritical("Error getting episodes!");
_logger.LogCritical(e.ToString());
_logger.LogCritical(e?.InnerException?.ToString());
throw;
}
finally
{
semaphoreSlim.Release();
}
}
}
public interface IShowDatabase
{
Task<List<Show>> GetShows();
}
I'll move a bunch of this into some generic helpers for myself, or I'll use Akavache, or I'll try another Polly Cache Policy implemented farther up the call stack! Thanks for reading my ramblings!
我将为自己自己将其转移到一些通用帮助程序中,或者使用Akavache ,或者尝试在调用堆栈的更远处实现另一个Polly Cache Policy! 感谢您阅读我的杂文!
UPDATE: Be sure to read the comments below AND my response in Part 2.
更新:请确保阅读下面的评论以及我在第2部分中的回复。
polly