diff --git a/Falcon.SugarApi.Test/CacheTest.cs b/Falcon.SugarApi.Test/CacheTest.cs new file mode 100644 index 0000000..b3c7561 --- /dev/null +++ b/Falcon.SugarApi.Test/CacheTest.cs @@ -0,0 +1,127 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Falcon.SugarApi.Cache; +using System.Diagnostics; + +namespace Falcon.SugarApi.Test +{ + [TestClass] + public class CacheTest + { + [TestMethod] + public void MemoryTest() { + var sw = new Stopwatch(); + var services = new ServiceCollection() as IServiceCollection; + services.AddSugarMemoryCache(op => { + + }); + var ts = new TimeSpan(0, 1, 0); + var strResult = ""; + int runTimes = 10000; + var sp = services.BuildServiceProvider().CreateScope().ServiceProvider; + var sc = sp.GetService(); + var sca = sp.GetService(); + + var key = Guid.NewGuid().ToString(); + var strValue = Guid.NewGuid().ToString(); + //字符串同步存储 + Assert.IsNotNull(sc); + sw.Restart(); + sc.SetStringValue(key, strValue, ts); + sw.Stop(); + Console.WriteLine($"SetStringValue 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + sw.Restart(); + strResult = sc.GetStringValue(key); + sw.Stop(); + Console.WriteLine($"GetStringValue 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + Assert.IsTrue(strResult == strValue); + strValue = Guid.NewGuid().ToString(); + sc.SetStringValue(key, strValue, ts); + strResult = sc.GetStringValue(key); + Assert.IsTrue(strResult == strValue); + sw.Restart(); + for (int i = 0; i < runTimes; i++) { + strResult = sc.GetStringValue(key); + Assert.IsTrue(strResult == strValue); + } + sw.Stop(); + Console.WriteLine($"GetStringValue {runTimes}次执行时间{sw.ElapsedMilliseconds}毫秒"); + + //字符串异步存储 + Assert.IsNotNull(sca); + sw.Restart(); + sca.SetStringValueAsync(key, strValue, ts).Wait(); + sw.Stop(); + Console.WriteLine($"SetStringValueAsync 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + + strResult = Guid.NewGuid().ToString(); + sw.Restart(); + strResult = sca.GetStringValueAsync(key).Result; + sw.Stop(); + Console.WriteLine($"GetStringValueAsync 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + Assert.IsTrue(strResult == strValue); + strValue = Guid.NewGuid().ToString(); + sca.SetStringValueAsync(key, strValue, ts).Wait(); + strResult = sca.GetStringValueAsync(key).Result; + Assert.IsTrue(strResult == strValue); + sw.Restart(); + for (int i = 0; i < 100; i++) { + strResult = sca.GetStringValueAsync(key).Result; + Assert.IsTrue(strResult == strValue); + } + sw.Stop(); + Console.WriteLine($"GetStringValueAsync {runTimes}次执行时间{sw.ElapsedMilliseconds}毫秒"); + + //对象同步存储 + var obj = new CacheTestModel { + Id = 1, Name = "name1" + }; + sw.Restart(); + sc.SetValue(key, obj, ts); + sw.Stop(); + Console.WriteLine($"SetValue 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + sw.Restart(); + var objResult = sc.GetValue(key); + sw.Stop(); + Console.WriteLine($"GetValue 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + Assert.IsTrue(obj.Equals(objResult)); + //对象异步存储 + sw.Restart(); + sca.SetValueAsync(key, obj, ts); + sw.Stop(); + Console.WriteLine($"SetValueAsync 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + objResult = null; + sw.Restart(); + objResult = sca.GetValueAsync(key).Result; + sw.Stop(); + Console.WriteLine($"GetValueAsync 一次执行时间{sw.ElapsedMilliseconds}毫秒"); + Assert.IsTrue(obj.Equals(objResult)); + + } + + + internal class CacheTestModel + { + public int Id { get; set; } + public string Name { get; set; } + + public override bool Equals(object? obj) { + Console.WriteLine("对象比较"); + if (obj == null) { + return false; + } + if (obj is CacheTestModel o) { + return o.Id == this.Id && o.Name == this.Name; + } + else { + return false; + } + } + } + } +} diff --git a/Falcon.SugarApi/Cache/DistributedCacheEntryOptionsWallPaper.cs b/Falcon.SugarApi/Cache/DistributedCacheEntryOptionsWallPaper.cs new file mode 100644 index 0000000..50c0d8d --- /dev/null +++ b/Falcon.SugarApi/Cache/DistributedCacheEntryOptionsWallPaper.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.Extensions.Caching.Distributed; + +namespace Falcon.SugarApi.Cache +{ + /// + /// 分布式缓冲选项 + /// + internal class DistributedCacheEntryOptionsWallPaper : DistributedCacheEntryOptions + { + /// + /// 通过过期时间段实例化DistributedCacheEntryOptions + /// + /// 时间段 + public DistributedCacheEntryOptionsWallPaper(TimeSpan timeSpan) { + this.AbsoluteExpirationRelativeToNow = timeSpan; + } + } +} diff --git a/Falcon.SugarApi/Cache/IServiceCollectionExtend.cs b/Falcon.SugarApi/Cache/IServiceCollectionExtend.cs new file mode 100644 index 0000000..fc8b22f --- /dev/null +++ b/Falcon.SugarApi/Cache/IServiceCollectionExtend.cs @@ -0,0 +1,42 @@ +using Falcon.SugarApi.JsonSerialize; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace Falcon.SugarApi.Cache +{ + /// + /// 服务集合扩展 + /// + public static class IServiceCollectionExtend + { + /// + /// 注册Redis缓冲服务器 + /// + /// 服务集合 + /// 缓冲配置 + /// 服务集合 + public static IServiceCollection AddSugarRedisCache(this IServiceCollection services, Action configRedis) { + services.AddSingleton(_ => new JsonSerializeFactory()); + services.AddStackExchangeRedisCache(configRedis); + services.AddSingleton(); + services.AddSingleton(); + return services; + } + + /// + /// 注册内存缓冲服务器 + /// + /// 服务结合 + /// 缓冲配置 + /// 服务结合 + public static IServiceCollection AddSugarMemoryCache(this IServiceCollection services, Action configAction) { + services.AddSingleton(_ => new JsonSerializeFactory()); + services.AddDistributedMemoryCache(configAction); + services.AddSingleton(); + services.AddSingleton(); + return services; + } + } +} diff --git a/Falcon.SugarApi/Cache/ISugarCache.cs b/Falcon.SugarApi/Cache/ISugarCache.cs new file mode 100644 index 0000000..22deafb --- /dev/null +++ b/Falcon.SugarApi/Cache/ISugarCache.cs @@ -0,0 +1,41 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Falcon.SugarApi.Cache +{ + /// + /// 提供数据缓冲支持 + /// + public interface ISugarCache + { + /// + /// 获取缓冲对象 + /// + /// 缓存键 + /// + T? GetValue([NotNull] string key) where T : class; + + /// + /// 获取缓存的字符串值 + /// + /// 缓存键 + /// 缓冲值 + string GetStringValue([NotNull] string key); + + /// + /// 设置对象缓冲 + /// + /// 缓冲键 + /// 缓冲值 + /// 缓冲时长 + void SetValue([NotNull] string key, T value, TimeSpan cacheTimeSpan) where T : class; + + /// + /// 缓存字符串值 + /// + /// 缓存键 + /// 缓冲值 + /// 缓冲时长 + void SetStringValue([NotNull] string key, string value, TimeSpan cacheTimeSpan); + } +} diff --git a/Falcon.SugarApi/Cache/ISugarCacheAsync.cs b/Falcon.SugarApi/Cache/ISugarCacheAsync.cs new file mode 100644 index 0000000..63dbeb0 --- /dev/null +++ b/Falcon.SugarApi/Cache/ISugarCacheAsync.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +namespace Falcon.SugarApi.Cache +{ + /// + /// 提供数据缓冲支持 + /// + public interface ISugarCacheAsync + { + /// + /// 获取字符串换重置 + /// + /// + /// + /// + Task GetValueAsync([NotNull] string key, CancellationToken cancellationToken = default) where T : class; + + /// + /// 获取字符串换重置 + /// + /// + /// + /// + Task GetStringValueAsync([NotNull] string key, CancellationToken cancellationToken = default); + + /// + /// 设置字符串缓冲 + /// + /// 缓冲键 + /// 缓冲值 + /// 缓冲时长 + /// + Task SetValueAsync([NotNull] string key, T value, TimeSpan cacheTimeSpan, CancellationToken cancellationToken = default) where T : class; + + /// + /// 设置字符串缓冲 + /// + /// 缓冲键 + /// 缓冲值 + /// 缓冲时长 + /// + Task SetStringValueAsync([NotNull] string key, string value, TimeSpan cacheTimeSpan, CancellationToken cancellationToken = default); + } +} diff --git a/Falcon.SugarApi/Cache/Readme.md b/Falcon.SugarApi/Cache/Readme.md new file mode 100644 index 0000000..3a65493 --- /dev/null +++ b/Falcon.SugarApi/Cache/Readme.md @@ -0,0 +1,6 @@ +## 缓冲器 +> 使用 `IServiceCollection.AddSugarRedisCache` 或 `IServiceCollection.AddSugarMemoryCache` 注册缓冲器即可 +> 在使用时注入 `ISugarCache`同步接口 或 `ISugarCacheAsync`异步接口 +> 接口的`Get`和`Set`方法分别对应获取和保存缓冲值。 + +> 在可能的情况下尽量使用异步方法,异步效率远远高于同步方法。 \ No newline at end of file diff --git a/Falcon.SugarApi/Cache/SugarCache.cs b/Falcon.SugarApi/Cache/SugarCache.cs new file mode 100644 index 0000000..3d24563 --- /dev/null +++ b/Falcon.SugarApi/Cache/SugarCache.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Falcon.SugarApi.JsonSerialize; +using Microsoft.Extensions.Caching; +using Microsoft.Extensions.Caching.Distributed; + +namespace Falcon.SugarApi.Cache +{ + /// + /// 缓冲器 + /// + public class SugarCache : ISugarCache, ISugarCacheAsync + { + /// + /// 内部缓冲提供器实现 + /// + public IDistributedCache CacheProvider { get; set; } + /// + /// 序列化提供器 + /// + public IJsonSerialize Json { get; } + + /// + /// 构造缓冲器 + /// + /// 基础分布式缓冲器 + /// Json序列化工厂 + public SugarCache(IDistributedCache cache, JsonSerializeFactory factory) { + this.CacheProvider = cache; + Json = factory.CreateJsonSerialize(); + } + + /// + public T? GetValue([NotNull] string key) where T : class { + var str = GetStringValue(key); + return str.IsNullOrEmpty() ? null : Json.Deserialize(str); + } + /// + public string GetStringValue([NotNull] string key) { + return this.CacheProvider.GetString(key); + } + /// + public void SetValue([NotNull] string key, T value, TimeSpan cacheTimeSpan) where T : class { + if (value == null) { + CacheProvider.Remove(key); + return; + } + var str = Json.Serialize(value); + CacheProvider.SetString(key, str); + } + + /// + public void SetStringValue([NotNull] string key, string value, TimeSpan cacheTimeSpan) { + CacheProvider.SetString(key, value, new DistributedCacheEntryOptionsWallPaper(cacheTimeSpan)); + } + + /// + public async Task GetValueAsync([NotNull] string key, CancellationToken cancellationToken = default) where T : class { + var str = await CacheProvider.GetStringAsync(key, cancellationToken); + if (str.IsNullOrEmpty()) { + return null; + } + return Json.Deserialize(str); + } + + /// + public async Task GetStringValueAsync([NotNull] string key, CancellationToken cancellationToken = default) { + return await CacheProvider.GetStringAsync(key, cancellationToken); + } + + /// + public async Task SetValueAsync([NotNull] string key, T value, TimeSpan cacheTimeSpan, CancellationToken cancellationToken = default) where T : class { + if (value == null) { + await CacheProvider.RemoveAsync(key, cancellationToken); + return; + } + var str = Json.Serialize(value); + await CacheProvider.SetStringAsync(key, str, cancellationToken); + } + + /// + public async Task SetStringValueAsync([NotNull] string key, string value, TimeSpan cacheTimeSpan, CancellationToken cancellationToken = default) { + if (value == null) { + await CacheProvider.RemoveAsync(key, cancellationToken); + return; + } + await CacheProvider.SetStringAsync(key, value, cancellationToken); + } + } +} diff --git a/Falcon.SugarApi/Falcon.SugarApi.csproj b/Falcon.SugarApi/Falcon.SugarApi.csproj index 1a63810..3445cb5 100644 --- a/Falcon.SugarApi/Falcon.SugarApi.csproj +++ b/Falcon.SugarApi/Falcon.SugarApi.csproj @@ -11,17 +11,21 @@ - + + + + +