快速体验 .NET9 提供的 HybridCache 混合缓存

发布于:2025-05-07 ⋅ 阅读:(11) ⋅ 点赞:(0)

.NET 9 引入了 HybridCache,这是一个新的高性能、分布式就绪的内存缓存实现,旨在为现代 Web 应用提供更高效的数据缓存机制。它结合了本地缓存(如 IMemoryCache)和分布式缓存(如 Redis、Garnet、SQL Server)的优点,支持自动分级存储和异步刷新,同时具备高并发下的性能优势。

特点

  • 混合缓存架构:结合本地内存缓存与分布式缓存,减少网络往返。
  • 自动分层:热数据保留在本地,冷数据回退到分布式存储。
  • 异步刷新:避免缓存击穿,提升用户体验。
  • 可扩展性:支持自定义序列化、键生成策略和分布式缓存提供者。
  • 集成 DI:开箱即用,支持依赖注入。

🚀 使用示例

示例1:默认方式使用

1.1 安装 nuget 包(如果尚未引入)

新建 asp.net core9 webapi 项目,添加 nuget 包:

dotnet add package Microsoft.Extensions.Caching.Hybrid
  • nuget 包下载,https://www.nuget.org/packages/Microsoft.Extensions.Caching.Hybrid

Microsoft.Extensions.Caching.Hybrid

1.2 注册 HybridCache 服务

Program.cs 中注册:

var builder = WebApplication.CreateBuilder(args);

// 添加 HybridCache
builder.Services.AddHybridCache();

var app = builder.Build();

1.3 使用 IHybridCache 缓存数据

以下是一个简单的控制器使用示例:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Hybrid;

[ApiController]
[Route("[controller]")]
public class WeatherForecastController(IHybridCache cache) : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var key = "weather_data";
        var result = await cache.GetOrCreateAsync(key, async (token) =>
        {
            // 模拟耗时操作,例如从数据库或 API 获取数据
            await Task.Delay(100, token);
            return new[] { "Sunny", "Cloudy", "Rainy" };
        });

        return Ok(result);
    }
}

1.4 配置 HybridCache(可选)

你可以在 appsettings.json 或代码中配置缓存行为:

builder.Services.AddHybridCache(options =>
{
    options.LocalCacheSizeLimit = 1024; // 设置本地缓存最大条目数
    options.DistributedCacheEntryOptions = new DistributedCacheEntryOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
    };
});

说明:上面的配置项,可依据实际情况自行调整。

示例2:注册 Garnet 为分布式缓存提供者

ASP.NET Core 9 中,你可以将 HybridCache 配置为使用 Garnet 作为其底层的分布式缓存提供者。Garnet 是微软开发的一个高性能、兼容 Redis 协议的键值存储系统,适用于需要高吞吐和低延迟的场景。

  • Garnet 官网,https://microsoft.github.io/garnet/

Garnet 官网

✅ 使用 HybridCache + Garnet 的步骤:

2.1 安装必要的 NuGet 包

dotnet add package Microsoft.Extensions.Caching.Hybrid
dotnet add package Garnet.Client

2.2 启动 Garnet 服务器(本地或远程)

确保你已经安装并运行了 Garnet 服务,可以通过以下方式启动:

# 假设已编译 Garnet 并进入目录
cd Garnet/bin/Release/net7.0
./garnet-server --port 6380

2.3 注册 Garnet 客户端和服务

你需要创建一个自定义的 IDistributedCache 实现来使用 Garnet,或者使用社区提供的适配器。

下面是一个简单示例,展示如何注册 Garnet 作为分布式缓存提供者:

  • a. 创建 Garnet 分布式缓存实现
using Garnet.Client;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

public class GarnetDistributedCache : IDistributedCache
{
    private readonly GarnetClient _client;

    public GarnetDistributedCache(GarnetClient client) => _client = client;

    public byte[] Get(string key) => _client.GetAsync(key).Result;

    public async Task<byte[]> GetAsync(string key, CancellationToken ct = default)
        => await _client.GetAsync(key, ct);

    public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
        => _client.SetAsync(key, value, GetExpiryFromOptions(options)).Wait();

    public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken ct = default)
        => await _client.SetAsync(key, value, GetExpiryFromOptions(options), ct);

    public void Refresh(string key)
        => _client.KeyExpireAsync(key, TimeSpan.FromMinutes(5)).Wait(); // 默认刷新为 5 分钟

    public async Task RefreshAsync(string key, CancellationToken ct = default)
    {
        var ttl = await _client.KeyTtlAsync(key, ct); // 获取当前剩余 TTL
        if (ttl.HasValue && ttl.Value > TimeSpan.Zero)
        {
            // 若已有 TTL,刷新为相同的持续时间
            await _client.KeyExpireAsync(key, ttl.Value, ct);
        }
        else
        {
            // 否则设置一个默认 TTL(可自定义)
            await _client.KeyExpireAsync(key, TimeSpan.FromMinutes(5), ct);
        }
    }

    public void Remove(string key) => _client.DeleteAsync(key).Wait();
    public Task RemoveAsync(string key, CancellationToken ct) => _client.DeleteAsync(key, ct);

    // 根据 DistributedCacheEntryOptions 获取过期时间
    private TimeSpan? GetExpiryFromOptions(DistributedCacheEntryOptions options)
    {
        if (options.AbsoluteExpirationRelativeToNow.HasValue)
            return options.AbsoluteExpirationRelativeToNow.Value;
        else if (options.SlidingExpiration.HasValue)
            return options.SlidingExpiration.Value;
        return null;
    }
}
  • b. 注册 Garnet 客户端和缓存服务
var builder = WebApplication.CreateBuilder(args);

// 添加 Garnet 客户端
builder.Services.AddSingleton<GarnetClient>(sp =>
{
    var client = new GarnetClient("localhost", 6380);
    client.Connect();
    return client;
});

// 添加 Garnet 缓存实现
builder.Services.AddSingleton<IDistributedCache, GarnetDistributedCache>();

// 添加 HybridCache,并使用 Garnet 作为分布式缓存后端
builder.Services.AddHybridCache(options =>
{
    options.LocalCacheSizeLimit = 500;
    options.LocalTtl = TimeSpan.FromSeconds(30);
    options.UseDistributedCache = true;
    options.DistributedTtl = TimeSpan.FromMinutes(5);
    options.UseSlidingExpiration = true; // 自动调用 Refresh/RefreshAsync
});

在注册 HybridCache 时启用 滑动过期策略,这样,当某个缓存项被访问时,系统会自动调用 RefreshAsync 方法来更新其在 Garnet 中的 TTL

2.4 在应用中使用 HybridCache

在控制器中使用缓存方式与之前一致。

[ApiController]
[Route("[controller]")]
public class SampleController(IHybridCache cache) : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> GetData()
    {
        var data = await cache.GetOrCreateAsync("sample-key", async (token) =>
        {
            // 模拟耗时获取数据
            await Task.Delay(200, token);
            return new[] { "Data1", "Data2" };
        });

        return Ok(data);
    }
}

🧠 小贴士

  • 性能优化:可以结合 HybridCacheLocalTtlDistributedTtl 控制缓存生命周期。
  • 序列化配置:可通过 AddHybridCache() 的选项自定义 ISerializer,例如使用 System.Text.JsonMessagePack
  • Garnet 集群支持:如果使用集群模式,请确保客户端正确配置路由逻辑。

ASP.NET Core 9HybridCache 中,LocalTtlDistributedTtl 是两个关键配置项,用于控制缓存数据的生命周期。它们分别作用于 本地缓存层分布式缓存层,使得你可以更精细地管理缓存的有效期。

🔍 配置说明

  1. LocalTtl(本地缓存过期时间)
  • 含义:设置缓存项在本地内存中保留的最大时间。
  • 用途:确保频繁访问的数据保留在本地,提高响应速度,同时避免本地缓存长时间不更新。
  • 类型:TimeSpan
  • 示例:
services.AddHybridCache(options =>
{
    options.LocalTtl = TimeSpan.FromSeconds(30); // 本地缓存最多保留 30 秒
});
  1. DistributedTtl(分布式缓存过期时间)
  • 含义:设置缓存项在分布式缓存中的最大存活时间。
  • 用途:控制整个缓存系统的最终一致性,适用于跨节点共享缓存的场景。
  • 类型:TimeSpan
  • 示例:
services.AddHybridCache(options =>
{
    options.DistributedTtl = TimeSpan.FromMinutes(5); // 分布式缓存最多保留 5 分钟
});

🔄 即使本地缓存已过期,系统也会尝试从分布式缓存加载最新数据;若分布式缓存也过期,则重新生成并写回两级缓存。

⚙️ 典型组合策略

场景 LocalTtl DistributedTtl 说明
高频读取、容忍短时旧数据 30s 5min 提高性能,减少后端压力
数据变化频繁、要求较新 5s 30s 快速更新本地缓存,保持整体一致性
只读数据、极少变更 null(永不过期) 24h 本地永久缓存 + 分布式定期更新

🧠 使用技巧

  • 设置 LocalTtl = null 表示本地缓存永不自动过期(仍可能被逐出以节省内存)。
  • 设置 DistributedTtl = null 表示分布式缓存项不会自动删除。
  • 如果只设置了 LocalTtl 而未设置 DistributedTtl,则分布式缓存默认与本地一致。
  • 可结合 AbsoluteExpirationRelativeToNowSlidingExpiration 等高级策略进行更复杂的控制。

通过合理配置 LocalTtlDistributedTtl,你可以灵活控制 HybridCache 的行为,实现以下目标:

  • 提升高频访问性能(利用本地缓存)
  • 减少对后端系统的冲击(利用分布式缓存)
  • 控制数据新鲜度(避免缓存脏读)

这是 HybridCache 强大灵活性的重要体现之一。

✅ 总结

HybridCache.NET 9 的一大亮点,适用于需要高性能、低延迟和分布式能力的现代应用。通过简单配置即可获得 本地 + 分布式 的双重优势,是替代传统 IMemoryCacheIDistributedCache 的理想选择。

通过将 HybridCacheGarnet 结合,你可以构建一个高性能、低延迟、支持分布式部署的缓存架构。这非常适合用于 微服务、API 网关、电商推荐 等对性能要求极高的场景。


网站公告

今日签到

点亮在社区的每一天
去签到