ArgMultiLevelCache 1.0.30
dotnet add package ArgMultiLevelCache --version 1.0.30
NuGet\Install-Package ArgMultiLevelCache -Version 1.0.30
<PackageReference Include="ArgMultiLevelCache" Version="1.0.30" />
<PackageVersion Include="ArgMultiLevelCache" Version="1.0.30" />
<PackageReference Include="ArgMultiLevelCache" />
paket add ArgMultiLevelCache --version 1.0.30
#r "nuget: ArgMultiLevelCache, 1.0.30"
#:package ArgMultiLevelCache@1.0.30
#addin nuget:?package=ArgMultiLevelCache&version=1.0.30
#tool nuget:?package=ArgMultiLevelCache&version=1.0.30
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)时会保持相同的过期时间
性能优化建议
- 合理设置缓存层级:
CacheFactory.Configure(options =>
{
// 内存缓存作为一级缓存(最快)
options.AddCacheLevel(new MemoryCache(), 1);
// Redis缓存作为二级缓存(较快,持久化)
options.AddCacheLevel(new RedisCache("localhost:6379"), 2);
// 可以添加更多层级,数字越小优先级越高
});
- 合理设置过期时间:
// 为不同类型的数据设置不同的过期时间
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)); // 临时会话
- 避免缓存穿透:
// 使用 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();
}
故障排除
常见问题
内存使用过高
- 原因:旧版本内存缓存没有自动清理机制
- 解决方案:升级到 v1.0.29+ 版本,新版本支持自动清理过期项
缓存同步时过期时间不一致
- 原因:旧版本在缓存同步时没有传递过期时间
- 解决方案:升级到 v1.0.29+ 版本,已修复此问题
多线程环境下的竞态条件
- 原因:旧版本 CacheFactory 的线程安全实现有缺陷
- 解决方案:升级到 v1.0.29+ 版本,使用双重检查锁定模式
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.DependencyInjection6.0.0+Microsoft.Extensions.Logging.Abstractions9.0.5+Microsoft.Extensions.Options6.0.0+StackExchange.Redis2.6.122+Newtonsoft.Json13.0.3+
配置建议
- 开发环境:使用内存缓存即可,便于调试
- 测试环境:使用内存缓存 + Redis 缓存组合
- 生产环境:建议使用多级缓存,并配置适当的过期时间
贡献
欢迎提交问题和贡献代码,请遵循项目的贡献指南。
报告问题
如果您发现任何问题,请在 GitHub 上创建 Issue,并提供以下信息:
- 使用的版本号
- 运行环境(.NET Framework/.NET Core 版本)
- 问题的详细描述和重现步骤
- 相关的错误日志
贡献代码
- Fork 项目
- 创建功能分支
- 提交更改
- 创建 Pull Request
感谢您使用 ArgMultiLevelCache!
| Product | Versions 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. |
-
.NETFramework 4.6.2
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Microsoft.Extensions.Options (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
-
.NETFramework 4.7.2
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Microsoft.Extensions.Options (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
-
.NETFramework 4.8
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Microsoft.Extensions.Options (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
-
.NETStandard 2.0
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Microsoft.Extensions.Options (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.