NET9 提供HybridCache解决分布式缓存中存在的网络链接&序列化带来的性能问题

下面是一个标准的IDistributedCache用例:

public class SomeService(IDistributedCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync
        (string name, int id, CancellationToken token = default)
    {
        var key = $"someinfo:{name}:{id}"; // Unique key for this combination.
        var bytes = await cache.GetAsync(key, token); // Try to get from cache.
        SomeInformation info;
        if (bytes is null)
        {
            // Cache miss; get the data from the real source.
            info = await SomeExpensiveOperationAsync(name, id, token);

            // Serialize and cache it.
            bytes = SomeSerializer.Serialize(info);
            await cache.SetAsync(key, bytes, token);
        }
        else
        {
            // Cache hit; deserialize it.
            info = SomeSerializer.Deserialize<SomeInformation>(bytes);
        }
        return info;
    }

    // This is the work we're trying to cache.
    private async Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
        CancellationToken token = default)
    { /* ... */ }
}

在这个用例中 每次都要做很多事情,包括序列化/反序列化。如果缓存不存在/未命中,可能会有多个并发线程获取基础数据并序列化数据,并将所有数据推送到缓存中间件中,我们看到这里 分布式缓存使用上就没有IMemoryCache那么性能高效, 为此.NET团队在NET9中 添加了 HybridCache解决这个痛点,

原理还是简单,使用本地的IMemoryCacheIDistributedCache包装一层,提供一个二级缓存包装的概念.

下面简单引用并注册一下服务:

<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
services.AddMemoryCache();
services.AddDistributedMemoryCache();//分布式缓存简单测试用的内存缓存,可以是Garnet,Redis等~
services.AddHybridCache(options =>
	{
	//options.MaximumPayloadBytes = 1*1024*1024 //默认超过1M的数据不会提供二级缓存,避免应用服务器内存负担
	options.DefaultEntryOptions = new HybridCacheEntryOptions{
	Expiration = TimeSpan.FromSeconds(5 * 60),//设置一个分布式缓存默认过期时间
	LocalCacheExpiration = TimeSpan.FromSeconds(5 * 60 - 1)//二级缓存默认过期时间,比前者短就行.或开发者可以接受的范围
	};
});

minimal api中的简单用例:


/// <summary>
/// 模拟的缓存数据类型
/// </summary>
public record CacheData(DateTime? DateTime);

//NET8中的IDistributedCache
x.MapGet("/cached-in-distribute", async (IDistributedCache distributedCache) =>
{
    if (await distributedCache.GetStringAsync("$cached-in-distribute") is null)
    {
        var data = System.Text.Json.JsonSerializer.Serialize(new CacheData(DateTime.Now));
        await distributedCache.SetStringAsync("$cached-in-distribute", data, new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10d)
        });
    }

    var fromCacheData = System.Text.Json.JsonSerializer.Deserialize<CacheData>(
        await distributedCache.GetStringAsync("$cached-in-distribute") ?? throw new Exception());
    return Results.Content($"{fromCacheData?.DateTime}-{DateTime.Now}");
});

//NET9 HybridCache,避免分布式缓存的强制转换
x.MapGet("/cached-in-hybrid", async (HybridCache hybridCache) =>
{
    var cachedDate = await hybridCache.GetOrCreateAsync($"$cached-in-hybrid", async cancel =>
        {
            return await ValueTask.FromResult(new CacheData(DateTime.Now));
        }, options: new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromSeconds(10d),//便于验证,设直10秒过期
            LocalCacheExpiration = TimeSpan.FromSeconds(10d),
        });
    return Results.Content($"缓存的数据:{cachedDate.DateTime}");
});

HybridCache通过本地的二级缓存避免了频繁的与分布式缓存服务器的交互以及成本高昂的类型转换(如果数据结构复杂庞大更甚),性能瞬间又提升了.

另外HybridCache是一个抽象类,微软默认的实现是二级到内存缓存,如果你有兴趣甚至可以无限封装扩展到其他的缓存中 比如你自己的YourHybridCache

services.TryAddSingleton<HybridCache, YourHybridCache>();

最后Microsoft.Extensions.Caching.Hybrid兼容.NET Framework 4.7.2 and .NET Standard 2.0这个也可以点个赞,对老系统升级比较友好!

更多信息:
https://learn.microsoft.com/en-us/aspnet/core/performance/caching/hybrid?view=aspnetcore-9.0
用例代码:
https://github.com/vipwan/Biwen.QuickApi/blob/net9/Biwen.QuickApi.DemoWeb/~DemoModular.cs

posted @ 2024-06-10 16:01  万雅虎  阅读(661)  评论(0编辑  收藏  举报