ArgMultiLevelCache 1.0.30

dotnet add package ArgMultiLevelCache --version 1.0.30
                    
NuGet\Install-Package ArgMultiLevelCache -Version 1.0.30
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ArgMultiLevelCache" Version="1.0.30" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ArgMultiLevelCache" Version="1.0.30" />
                    
Directory.Packages.props
<PackageReference Include="ArgMultiLevelCache" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ArgMultiLevelCache --version 1.0.30
                    
#r "nuget: ArgMultiLevelCache, 1.0.30"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ArgMultiLevelCache@1.0.30
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ArgMultiLevelCache&version=1.0.30
                    
Install as a Cake Addin
#tool nuget:?package=ArgMultiLevelCache&version=1.0.30
                    
Install as a Cake Tool

ArgMultiLevelCache 多级缓存类库

项目简介

ArgMultiLevelCache 是一个高性能、线程安全的.NET多级缓存类库,支持多种缓存策略的组合使用。通过分层缓存机制,可以有效地平衡性能和资源使用。

主要功能

  • 多级缓存管理:支持内存缓存和Redis缓存的分层组合
  • 自动内存管理:内存缓存支持过期项自动清理,防止内存泄漏
  • 线程安全设计:所有组件都经过线程安全优化
  • 资源管理:正确实现 IDisposable 接口,确保资源及时释放
  • 灵活的缓存策略配置:支持自定义过期时间和缓存层级
  • 基于事件的发布订阅机制:支持本地和分布式事件(已迁移至 ArgEventBus 项目)
  • 多平台支持:支持.NET Framework 4.6.2+、.NET Standard 2.0 和 .NET Core/.NET 5+

版本更新说明

v1.0.29+ 重要改进

  • 修复内存泄漏:内存缓存新增自动清理机制,每5分钟清理过期项
  • 修复缓存同步问题:修复多级缓存同步时过期时间丢失的问题
  • 改进资源管理:所有缓存实现正确实现 IDisposable 接口
  • 线程安全优化:重构 CacheFactory 使用双重检查锁定模式
  • 代码质量提升:启用 nullable 引用类型,消除所有编译警告
  • 完善文档注释:为所有公共方法添加完整的函数级注释

使用方法

包导入

dotnet add package ArgMultiLevelCache

平台使用说明

.NET Core/.NET 5+

在.NET Core和.NET 5+平台中,推荐使用依赖注入方式配置和使用缓存:

// 在Startup.cs中配置服务
public void ConfigureServices(IServiceCollection services)
{
    services.AddMultiLevelCache(options =>
    {
        options.DefaultExpiration = TimeSpan.FromMinutes(30);
        options.AddCacheLevel(new MemoryCache(), 1);
        options.AddCacheLevel(new RedisCache("localhost:6379"), 2);
    });
    
    // 事件服务(已迁移至 ArgEventBus 项目)
    services.AddEventServices();
    services.AddRedisEventServices("localhost:6379");
}

// 在服务中使用
public class UserService
{
    private readonly IMultiLevelCache _cache;
    private readonly ArgEventBus.PubSub.RedisEventManager _eventManager; // 使用Redis事件管理器

    public UserService(IMultiLevelCache cache, ArgEventBus.PubSub.RedisEventManager eventManager)
    {
        _cache = cache;
        _eventManager = eventManager;
    }

    public async Task<UserData> GetUserAsync(string userId)
    {
        return await _cache.GetAsync<UserData>(
            $"user:{userId}",
            async () => await _userRepository.GetByIdAsync(userId)
        );
    }
    
    public async Task InvalidateUserCacheAsync(string userId, string reason)
    {
        // 发布缓存失效事件
        await _eventManager.PublishEventAsync(new CacheInvalidatedEventArgs
        {
            Key = $"user:{userId}",
            InvalidatedTime = DateTime.UtcNow,
            Reason = reason
        });
        
        // 直接从缓存中移除
        await _cache.RemoveAsync($"user:{userId}");
    }
}
.NET Framework

在.NET Framework平台中,推荐使用静态工厂类进行全局访问,确保线程安全:

// 在应用程序启动时配置缓存(如Global.asax.cs中)
CacheFactory.Configure(options =>
{
    options.DefaultExpiration = TimeSpan.FromMinutes(30);
    options.AddCacheLevel(new MemoryCache(), 1);                // 一级缓存:内存缓存
    options.AddCacheLevel(new RedisCache("localhost:6379"), 2); // 二级缓存:Redis缓存
});

// 初始化Redis事件管理器
ArgEventBus.PubSub.RedisEventManager.Initialize("localhost:6379");

// 在任意位置使用缓存(线程安全)
public class UserService
{
    public async Task<UserData> GetUserAsync(string userId)
    {
        return await CacheFactory.Default.GetAsync<UserData>(
            $"user:{userId}",
            async () => await GetUserFromDatabaseAsync(userId)
        );
    }

    private async Task<UserData> GetUserFromDatabaseAsync(string userId)
    {
        // 从数据库获取用户数据的实现
        return await Task.FromResult(new UserData());
    }
    
    public async Task InvalidateUserCacheAsync(string userId)
    {
        // 发布缓存失效事件
        await ArgEventBus.PubSub.RedisEventManager.Instance.PublishEventAsync(new CacheInvalidatedEventArgs
        {
            Key = $"user:{userId}",
            InvalidatedTime = DateTime.UtcNow,
            Reason = "用户数据更新"
        });
    }
}

// 在WebForm页面中使用
public partial class UserProfile : System.Web.UI.Page
{
    private UserService _userService = new UserService();

    protected async void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            await LoadUserDataAsync();
        }
    }

    private async Task LoadUserDataAsync()
    {
        var userId = Request.QueryString["userId"];
        var userData = await _userService.GetUserAsync(userId);
        // 使用userData更新页面控件
    }
}

事件发布订阅

本地事件

本地事件使用 EventManager 类进行管理,适用于单实例应用:

// 定义事件参数类
public class UserUpdatedEventArgs
{
    public string UserId { get; set; }
    public DateTime UpdateTime { get; set; }
}

// 实现事件观察者
public class UserCacheInvalidator : IEventObserver<UserUpdatedEventArgs>
{
    // 定义观察者执行顺序
    public int Order => 1;
    
    // 实现事件处理方法
    public async Task HandleEventAsync(UserUpdatedEventArgs eventArgs)
    {
        // 处理用户更新事件,例如清除缓存
        Console.WriteLine($"用户 {eventArgs.UserId} 在 {eventArgs.UpdateTime} 被更新");
        await Task.CompletedTask;
    }
}

// 发布事件
await ArgEventBus.PubSub.EventManager.Instance.PublishEventAsync(new UserUpdatedEventArgs
{
    UserId = "123",
    UpdateTime = DateTime.UtcNow
});

分布式事件(Redis)

分布式事件使用 RedisEventManager 类进行管理,适用于多实例分布式应用:

// 定义事件参数类(与本地事件相同)
public class UserUpdatedEventArgs
{
    public string UserId { get; set; }
    public DateTime UpdateTime { get; set; }
}

// 实现事件观察者(与本地事件相同)
public class UserCacheInvalidator : IEventObserver<UserUpdatedEventArgs>
{
    public int Order => 1;
    
    public async Task HandleEventAsync(UserUpdatedEventArgs eventArgs)
    {
        // 处理用户更新事件
        Console.WriteLine($"用户 {eventArgs.UserId} 在 {eventArgs.UpdateTime} 被更新");
        await Task.CompletedTask;
    }
}

// 发布分布式事件
await ArgEventBus.PubSub.RedisEventManager.Instance.PublishEventAsync(new UserUpdatedEventArgs
{
    UserId = "123",
    UpdateTime = DateTime.UtcNow
});

自动确认机制

RedisEventManager 支持事件的自动确认机制,确保事件被正确处理:

// 在Startup.cs中配置服务
public void ConfigureServices(IServiceCollection services)
{
    // 添加Redis事件服务,启用自动确认
    services.AddRedisEventServices("localhost:6379", enableAutoAck: true);
}

// 实现支持自动确认的观察者
public class AutoAckObserver : IEventObserver<SomeEventArgs>
{
    public int Order => 1;
    
    public async Task<bool> HandleEventAsync(SomeEventArgs eventArgs)
    {
        try {
            // 处理事件逻辑
            await DoSomethingAsync(eventArgs);
            
            // 返回true表示处理成功,事件将被确认
            return true;
        }
        catch {
            // 返回false表示处理失败,事件不会被确认,将被重新处理
            return false;
        }
    }
}

资源管理和最佳实践

内存缓存自动清理

从 v1.0.29+ 开始,内存缓存支持自动清理过期项,有效防止内存泄漏:

// 内存缓存会自动每5分钟清理一次过期项
var memoryCache = new MemoryCache();

// 设置缓存项,1分钟后过期
await memoryCache.SetAsync("key1", "value1", TimeSpan.FromMinutes(1));

// 过期项会在下次清理周期中自动移除
// 无需手动干预

正确的资源释放

所有缓存实现都正确实现了 IDisposable 接口:

// 在应用程序关闭时正确释放资源
public class Application
{
    private readonly MemoryCache _memoryCache;
    private readonly RedisCache _redisCache;
    
    public Application()
    {
        _memoryCache = new MemoryCache();
        _redisCache = new RedisCache("localhost:6379");
    }
    
    // 应用程序关闭时调用
    public void Shutdown()
    {
        _memoryCache?.Dispose(); // 释放定时器和清理内存
        _redisCache?.Dispose();  // 释放Redis连接
    }
}

// 或者使用 using 语句自动释放
using var cache = new MemoryCache();
await cache.SetAsync("key", "value");
// cache 会在作用域结束时自动释放

线程安全使用

CacheFactory 已经过线程安全优化,可以在多线程环境中安全使用:

// 多线程环境中安全使用
public class MultiThreadService
{
    public async Task ProcessDataAsync(string key, object data)
    {
        // CacheFactory.Default 是线程安全的
        await CacheFactory.Default.SetAsync(key, data);
        
        // 多个线程同时访问不会有问题
        var result = await CacheFactory.Default.GetAsync<object>(key);
    }
}

// 并发访问示例
var tasks = Enumerable.Range(0, 100).Select(async i =>
{
    await CacheFactory.Default.SetAsync($"key{i}", $"value{i}");
    return await CacheFactory.Default.GetAsync<string>($"key{i}");
});

var results = await Task.WhenAll(tasks);

缓存同步优化

修复了多级缓存同步时过期时间丢失的问题:

// 现在缓存同步会正确传递过期时间
var cache = CacheFactory.Default;

// 从数据源获取数据,设置1小时过期
var userData = await cache.GetAsync<UserData>(
    "user:123",
    async () => await GetUserFromDatabase("123"),
    TimeSpan.FromHours(1) // 过期时间会正确同步到所有缓存层
);

// 当从高级缓存(如内存)找到数据时
// 同步到低级缓存(如Redis)时会保持相同的过期时间

性能优化建议

  1. 合理设置缓存层级
CacheFactory.Configure(options =>
{
    // 内存缓存作为一级缓存(最快)
    options.AddCacheLevel(new MemoryCache(), 1);
    
    // Redis缓存作为二级缓存(较快,持久化)
    options.AddCacheLevel(new RedisCache("localhost:6379"), 2);
    
    // 可以添加更多层级,数字越小优先级越高
});
  1. 合理设置过期时间
// 为不同类型的数据设置不同的过期时间
await cache.SetAsync("user:profile", userData, TimeSpan.FromMinutes(30));    // 用户资料
await cache.SetAsync("system:config", config, TimeSpan.FromHours(24));       // 系统配置
await cache.SetAsync("temp:session", session, TimeSpan.FromMinutes(5));      // 临时会话
  1. 避免缓存穿透
// 使用 dataFetcher 参数避免缓存穿透
var result = await cache.GetAsync<UserData>(
    $"user:{userId}",
    async () => {
        var user = await _userRepository.GetByIdAsync(userId);
        // 即使查询结果为空,也会被缓存,避免重复查询数据库
        return user;
    },
    TimeSpan.FromMinutes(10)
);

高级功能

自定义序列化

可以为Redis缓存和事件管理器配置自定义的序列化设置:

// 配置自定义序列化设置
var jsonSettings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    NullValueHandling = NullValueHandling.Ignore
};

// 使用自定义序列化设置初始化Redis事件管理器
RedisEventManager.Initialize("localhost:6379", jsonSettings);

事件重试机制

RedisEventManager 支持事件处理失败时的重试机制:

// 在Startup.cs中配置服务
public void ConfigureServices(IServiceCollection services)
{
    // 添加Redis事件服务,配置重试策略
    services.AddRedisEventServices(options => {
        options.ConnectionString = "localhost:6379";
        options.EnableAutoAck = true;
        options.MaxRetryCount = 3;
        options.RetryDelayMs = 1000; // 1秒后重试
    });
}
//非redis
public void ConfigureServices(IServiceCollection services)
{
    // 添加事件服务,配置重试策略
    services.AddEventServices();
}

故障排除

常见问题

  1. 内存使用过高

    • 原因:旧版本内存缓存没有自动清理机制
    • 解决方案:升级到 v1.0.29+ 版本,新版本支持自动清理过期项
  2. 缓存同步时过期时间不一致

    • 原因:旧版本在缓存同步时没有传递过期时间
    • 解决方案:升级到 v1.0.29+ 版本,已修复此问题
  3. 多线程环境下的竞态条件

    • 原因:旧版本 CacheFactory 的线程安全实现有缺陷
    • 解决方案:升级到 v1.0.29+ 版本,使用双重检查锁定模式
  4. Redis 连接未正确释放

    • 原因:RedisCache 没有正确实现 IDisposable 接口
    • 解决方案:升级到 v1.0.29+ 版本,并确保在应用程序关闭时调用 Dispose()

性能监控

// 监控缓存命中率
public class CacheMetrics
{
    private static long _hits = 0;
    private static long _misses = 0;
    
    public static void RecordHit() => Interlocked.Increment(ref _hits);
    public static void RecordMiss() => Interlocked.Increment(ref _misses);
    
    public static double GetHitRate()
    {
        var total = _hits + _misses;
        return total == 0 ? 0 : (double)_hits / total;
    }
}

// 在缓存使用中集成监控
public async Task<T> GetWithMetricsAsync<T>(string key, Func<Task<T>> dataFetcher)
{
    var result = await CacheFactory.Default.GetAsync<T>(key);
    
    if (result != null)
    {
        CacheMetrics.RecordHit();
        return result;
    }
    
    CacheMetrics.RecordMiss();
    var data = await dataFetcher();
    await CacheFactory.Default.SetAsync(key, data);
    return data;
}

注意事项

版本兼容性

  • v1.0.29+:推荐版本,包含所有重要修复
  • v1.0.28 及以下:存在内存泄漏和线程安全问题,建议升级

平台要求

  • .NET Framework:4.6.2 或更高版本
  • .NET Standard:2.0 或更高版本
  • .NET Core/.NET 5+:所有版本

依赖项

  • Microsoft.Extensions.DependencyInjection 6.0.0+
  • Microsoft.Extensions.Logging.Abstractions 9.0.5+
  • Microsoft.Extensions.Options 6.0.0+
  • StackExchange.Redis 2.6.122+
  • Newtonsoft.Json 13.0.3+

配置建议

  • 开发环境:使用内存缓存即可,便于调试
  • 测试环境:使用内存缓存 + Redis 缓存组合
  • 生产环境:建议使用多级缓存,并配置适当的过期时间

贡献

欢迎提交问题和贡献代码,请遵循项目的贡献指南。

报告问题

如果您发现任何问题,请在 GitHub 上创建 Issue,并提供以下信息:

  • 使用的版本号
  • 运行环境(.NET Framework/.NET Core 版本)
  • 问题的详细描述和重现步骤
  • 相关的错误日志

贡献代码

  1. Fork 项目
  2. 创建功能分支
  3. 提交更改
  4. 创建 Pull Request

感谢您使用 ArgMultiLevelCache!

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.30 210 9/29/2025
1.0.29 241 5/29/2025