c# - 使用 ASP.NET Core 3.1 自定义分布式缓存进行响应缓存
问题描述
我编写了一个基于 Azure BlobStorage 的自定义分布式缓存来优化页面速度。网站应该从缓存中传递缓存页面,直到缓存页面过期。这个实现应该像现有的 DistributedInMemoryCache、DistributedRedisCache 和 NCacheDistributedCache 一样工作。此处描述了操作方法https://docs.microsoft.com/de-de/aspnet/core/performance/caching/distributed?view=aspnetcore-5.0
我的问题是,在我的缓存中获取或设置的方法没有执行。
我将 IDistributedCache 实现为 DistributedBlobStorageCache 并在 ServiceCollection 扩展AddDistributedBlobStorageCache()的帮助下注册了它。所以票价这么好。
该操作具有上面的ResponseCacheAttribute,并且缓存配置文件在 Startup.cs 中设置。据我了解,系统配置是否正确,但执行分布式缓存的Get/GetAsync或Set/SetAsync方法。
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddAntiforgery();
services.AddResponseCaching();
services.AddDistributedBlobStorageCache(options =>
{
options.ConnectionString = "<my connection string>";
});
services.AddResponseCompression();
services.AddHttpsRedirection(options => options.RedirectStatusCode = 301);
services.AddControllersWithViews(
options =>
{
options.RespectBrowserAcceptHeader = true;
options.CacheProfiles.Add(new KeyValuePair<string, CacheProfile>("test", new CacheProfile
{
Duration = 60
}));
// authorization filters
options.Filters.Add<AutoValidateAntiforgeryTokenAttribute>();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseResponseCaching();
app.UseResponseCompression();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
public static class BlobStorageCacheServiceCollectionExtensions
{
public static IServiceCollection AddDistributedBlobStorageCache(this IServiceCollection services, Action<BlobStorageCacheOptions> options)
{
if (options != default)
{
services.AddOptions();
services.Configure(options);
}
return services.AddSingleton<IDistributedCache, DistributedBlobStorageCache>();
}
}
public class BlobStorageCacheOptions : DistributedCacheEntryOptions
{
public string ConnectionString { get; set; }
}
public class DistributedBlobStorageCache : IDistributedCache
{
private readonly ILoggerFactory _loggerFactory;
private readonly BlobStorageCacheOptions _options;
public DistributedBlobStorageCache(ILoggerFactory loggerFactory, IOptions<BlobStorageCacheOptions> optionsAccessor)
{
_loggerFactory = loggerFactory;
_options = optionsAccessor?.Value;
}
public byte[] Get(string key)
{
return GetAsync(key).GetAwaiter().GetResult();
}
public async Task<byte[]> GetAsync(string key, CancellationToken token = new CancellationToken())
{
var repos = CreateRepository();
var cacheItem = await repos.GetAsync(key, token);
if (cacheItem == null || cacheItem.ContentBytes == null)
return Array.Empty<byte>();
return cacheItem.ContentBytes;
}
public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
{
SetAsync(key, value, options).GetAwaiter().GetResult();
}
public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options,
CancellationToken token = new CancellationToken())
{
var cacheItem = new CacheItem
{
ContentBytes = value,
Key = key,
UtcExpiry = options.AbsoluteExpiration.GetValueOrDefault(DateTimeOffset.UtcNow).DateTime
};
var repos = CreateRepository();
await repos.SaveAsync(cacheItem, token);
}
public void Refresh(string key)
{
// not needed, because we use no sliding expiration
}
public Task RefreshAsync(string key, CancellationToken token = new CancellationToken())
{
// not needed, because we use no sliding expiration
return Task.CompletedTask;
}
public void Remove(string key)
{
RemoveAsync(key).GetAwaiter().GetResult();
}
public async Task RemoveAsync(string key, CancellationToken token = new CancellationToken())
{
var repos = CreateRepository();
await repos.RemoveAsync(key, token);
}
private BlobStorageCacheRepository CreateRepository()
{
return new BlobStorageCacheRepository(_options.ConnectionString);
}
private class BlobStorageCacheRepository
{
public BlobStorageCacheRepository(string connectionString)
{
}
internal Task<CacheItem> GetAsync(string key, CancellationToken token)
{
// to implement
return Task.FromResult(new CacheItem());
}
internal Task SaveAsync(CacheItem item, CancellationToken token)
{
// to implement
return Task.CompletedTask;
}
internal Task RemoveAsync(string key, CancellationToken token)
{
// to implement
return Task.CompletedTask;
}
}
private class CacheItem
{
internal byte[] ContentBytes { get; set; }
internal string Key { get; set; }
internal DateTimeOffset UtcExpiry { get; set; }
}
}
解决方案
实际上,您正在做的是两件不同的事情
关于响应缓存
services.AddResponseCaching()
并且app.UseResponseCaching()
用于注册ObjectPoolProvider
和ResponseCachingMiddleware
相应的,这个中间件负责缓存我们的动作端点,它曾经有过[ResponseCache]
它。
这个中间件没有IDistributedCache
我们上面提到的依赖。因此,两个不相关的东西,无论如何都没有任何关系。
关于IDistributedCache
这是作为准系统设计的,DistributedCache
因为 .net 核心团队认为它非常适合大多数情况。IDistributedCache
可以通过许多机制来实现,包括 RMDB、DocumentDb、Redis……,甚至在这种情况下,还有 blob 存储。但是,请记住,准系统对实现细节一无所知(如果我们不实现该机制,则创建一个滑动过期的条目选项什么都不做)。
例如:
MemoryCache
实现IMemoryCache
(可以在 Microsoft.Extensions.Caching.Memory 中找到)的实例,具有Set<TItem>
处理存储数据的过程的扩展,并为此设置选项条目(如过期时间),正如我们在此处看到的那样。
所以,即使我在 上看到了你的实现IDistributedCache
,但我没有看到任何关于处理过期的东西,所以......那是行不通的......即使我们尝试直接使用它。
没有魔法,这就是代码的实现方式。
推荐阅读
- django - 迁移文件太大阻止迁移
- assembly - 无法在 Linux NASM 中打印单个字符
- php - 如何通过正则表达式提取 youtube 的 m3u8?
- ruby-on-rails - Rails 5.2.3 需要哪个版本的 Ruby?
- mongodb - MongoDB Atlas 导入 json 文件 - 出现错误
- react-native - 在周日反应原生日历禁用触摸事件
- r - 将相同条件应用于 case_when 函数内的多个变量的替代方法
- javascript - 你能用 Hooks 添加一个组件来声明状态吗?
- java - javafx 的 Swing 中是否有 ComboBoxModel 的等价物?
- azure-devops - 在 Azure DevOps sprint 项目中 - 有没有一种方法可以与团队共享评论,而无需客户看到?