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

net9,hybridcache · 浏览次数 : 0

小编点评

在分布式系统中,为了避免每次请求都进行昂贵的远程调用和数据序列化/反序列化操作,可以使用缓存来提高性能。在.NET中,微软提供了分布式缓存接口`IDistributedCache`,但是在某些情况下,直接使用`IDistributedCache`可能不够高效。 为了解决这个问题,NET团队在NET9中引入了`HybridCache`,它是在本地内存缓存`IMemoryCache`上包装一层,提供了一个第二级缓存的概念。`HybridCache`的主要优点是避免了频繁与分布式缓存服务器的交互和类型转换的开销,从而提高了性能。 以下是一个使用HybridCache的示例: ```csharp // 在.NET的Microsoft.Extensions.Caching命名空间下 // 使用示例:Microsoft.Extensions.Caching.Hybrid // 引入Microsoft.Extensions.Caching命名空间 using Microsoft.Extensions.Caching; // 注册HybridCache服务 services.AddMemoryCache(); services.AddDistributedMemoryCache(); services.AddHybridCache(options => { // 设置最大本地缓存大小(单位:字节) options.MaximumPayloadBytes = 1 * 1024 * 1024; // 设置本地和分布式缓存的默认过期时间 options.DefaultEntryOptions = new HybridCacheEntryOptions { Expiration = TimeSpan.FromSeconds(5 * 60), LocalCacheExpiration = TimeSpan.FromSeconds(5 * 60 - 1) }; }); // 实现HybridCache的抽象类 public class MyHybridCache : HybridCache { public MyHybridCache(IMemoryCache memoryCache, IOptions options) : base(memoryCache, options) { } // 可以在这里根据需要添加自定义逻辑 } // 使用HybridCache public class SomeService { private readonly Hybrids.MyHybridCache _cache; public SomeService(IHostEnvironment hostEnvironment) { _cache = new MyHybridCache(hostEnvironment.Services.GetRequiredService(), new HybridCacheEntryOptions()); } public async Task GetSomeInformationAsync(string name, int id, CancellationToken token = default) { var key = $@"someinfo:{name}:{id}"; var bytes = await _cache.GetAsync(key, token); SomeInformation info; if (bytes == null) { // 缓存未命中,从真实来源获取数据 info = await SomeExpensiveOperationAsync(name, id, token); // 序列化和缓存数据 bytes = SomeSerializer.Serialize(info); await _cache.SetAsync(key, bytes, token); } else { // 缓存命中,反序列化数据 info = SomeSerializer.Deserialize(bytes); } return info; } private async Task SomeExpensiveOperationAsync(string name, int id, CancellationToken token = default) { // ... 购物车校验、库存校验 等操作 } } ``` 总之,HybridCache通过本地二级缓存避免了与分布式缓存服务器的频繁交互和类型转换的开销,从而提高了整体性能。如果你的应用程序在.NET 4.7.2或.NET Standard 2.0上运行,也可以使用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();//分布式缓存这里默认本地内存缓存,可以是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)//本地二级缓存默认过期时间,比前者短就行.或者你认为可以接受的范围.
	};
});

/// <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

与NET9 提供HybridCache解决分布式缓存中存在的远程链接&序列化带来的性能问题相似的内容:

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

下面是一个标准的IDistributedCache用例: public class SomeService(IDistributedCache cache) { public async Task GetSomeInformationAsync (string na

NET9 AspnetCore将整合OpenAPI的文档生成功能而无需三方库

OpenAPI 规范是用于描述 HTTP API 的标准。该标准允许开发人员定义 API 的形状,这些 API 可以插入到客户端生成器、服务器生成器、测试工具、文档等中。尽管该标准具有普遍性和普遍性,但 ASP.NET Core 在框架内默认不提供对 OpenAPI 的支持。 当前 ASP.NET

WPF在.NET9中的重大更新:Windows 11 主题

在2023年的2月20日,在WPF的讨论区,WPF团队对路线的优先级发起了一次讨论。 对三个事项发起了投票。 第一个是Windows 11 主题 第二个是更新的控件 第三个是可空性注释 最终Windows 11 主题得票最高,WPF团队2023-2024的工作优先级就是Windows 11 主题了。

C# 13(.Net 9) 中的新特性 - 扩展类型

C# 13 即 .Net 9 按照计划会在2024年11月发布,目前一些新特性已经定型,今天让我们来预览一个比较大型比较重要的新特性: 扩展类型 extension types

本计划在 .NET 8 中推出的 WASI 推迟到 .NET 9

本计划在 .NET 8 中推出的 WASI 已推迟到 .NET 9,请参阅 Github 上的 WASI 跟踪问题。 在.NET 8 Preview 4 开始支持生成与 WASI 兼容的 .wasm 文件,使用独立的 WebAssembly 运行时 Wasmtime CLI[1] 运行该文件。去年的

微软在Microsoft Build 2024 上 发布了.NET 9 预览版4

在 Microsoft Build 2024 上,与往年一样,.NET 不是会议主题演讲的主题,但是微软在这个大会上为.NET 推出一组新的功能和工具,旨在使 .NET 开发更快、更轻松,具体内容可以阅读文章:Microsoft Build 2024 的 .NET 公告和更新[1]。最新功能都在.N