From 5857f65601964845d782fd55a62f279574b0349e Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 13 Jun 2024 11:03:46 +0800 Subject: [PATCH] Enhance AbpRedisCahce --- .../StackExchangeRedis/AbpRedisCache.cs | 169 +++++++++++------- .../StackExchangeRedis/AbpRedisExtensions.cs | 16 +- 2 files changed, 110 insertions(+), 75 deletions(-) diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs index 8b59282cf12..c5ae0b5393b 100644 --- a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; @@ -19,6 +20,8 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems protected static readonly string SlidingExpirationKey; protected static readonly string DataKey; protected static readonly long NotPresent; + protected static readonly RedisValue[] HashMembersAbsoluteExpirationSlidingExpirationData; + protected static readonly RedisValue[] HashMembersAbsoluteExpirationSlidingExpiration; private readonly static FieldInfo SetScriptField; private readonly static FieldInfo RedisDatabaseField; @@ -27,29 +30,29 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems private readonly static MethodInfo MapMetadataMethod; private readonly static MethodInfo GetAbsoluteExpirationMethod; private readonly static MethodInfo GetExpirationInSecondsMethod; + private readonly static MethodInfo OnRedisErrorMethod; - protected IDatabase RedisDatabase => GetRedisDatabase()!; - private IDatabase? _redisDatabase; - - protected string Instance { get; } + protected RedisKey InstancePrefix { get; } static AbpRedisCache() { var type = typeof(RedisCache); - RedisDatabaseField = Check.NotNull(type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic), nameof(RedisDatabaseField))!; + RedisDatabaseField = Check.NotNull(type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic), nameof(RedisDatabaseField)); - SetScriptField = Check.NotNull(type.GetField("_setScript", BindingFlags.Instance | BindingFlags.NonPublic), nameof(SetScriptField))!; + SetScriptField = Check.NotNull(type.GetField("_setScript", BindingFlags.Instance | BindingFlags.NonPublic), nameof(SetScriptField)); - ConnectMethod = Check.NotNull(type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectMethod))!; + ConnectMethod = Check.NotNull(type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectMethod)); - ConnectAsyncMethod = Check.NotNull(type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectAsyncMethod))!; + ConnectAsyncMethod = Check.NotNull(type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectAsyncMethod)); - MapMetadataMethod = Check.NotNull(type.GetMethod("MapMetadata", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static), nameof(MapMetadataMethod))!; + MapMetadataMethod = Check.NotNull(type.GetMethod("MapMetadata", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static), nameof(MapMetadataMethod)); - GetAbsoluteExpirationMethod = Check.NotNull(type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetAbsoluteExpirationMethod))!; + GetAbsoluteExpirationMethod = Check.NotNull(type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetAbsoluteExpirationMethod)); - GetExpirationInSecondsMethod = Check.NotNull(type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetExpirationInSecondsMethod))!; + GetExpirationInSecondsMethod = Check.NotNull(type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetExpirationInSecondsMethod)); + + OnRedisErrorMethod = Check.NotNull(type.GetMethod("OnRedisError", BindingFlags.Instance | BindingFlags.NonPublic), nameof(OnRedisErrorMethod)); AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!; @@ -58,32 +61,29 @@ static AbpRedisCache() DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!; NotPresent = type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.To(); + + HashMembersAbsoluteExpirationSlidingExpirationData = [AbsoluteExpirationKey, SlidingExpirationKey, DataKey]; + + HashMembersAbsoluteExpirationSlidingExpiration = [AbsoluteExpirationKey, SlidingExpirationKey]; } public AbpRedisCache(IOptions optionsAccessor) : base(optionsAccessor) { - Instance = optionsAccessor.Value.InstanceName ?? string.Empty; - } - - protected virtual void Connect() - { - if (GetRedisDatabase() != null) + var instanceName = optionsAccessor.Value.InstanceName; + if (!string.IsNullOrEmpty(instanceName)) { - return; + InstancePrefix = (RedisKey)Encoding.UTF8.GetBytes(instanceName); } + } - ConnectMethod.Invoke(this, Array.Empty()); + protected virtual IDatabase Connect() + { + return (IDatabase)ConnectMethod.Invoke(this, Array.Empty())!; } protected virtual async ValueTask ConnectAsync(CancellationToken token = default) { - var redisDatabase = GetRedisDatabase(); - if (redisDatabase != null) - { - return redisDatabase; - } - return await (ValueTask)ConnectAsyncMethod.Invoke(this, new object[] { token })!; } @@ -108,9 +108,17 @@ public void SetMany( IEnumerable> items, DistributedCacheEntryOptions options) { - Connect(); + var cache = Connect(); - Task.WaitAll(PipelineSetMany(items, options)); + try + { + Task.WaitAll(PipelineSetMany(cache, items, options)); + } + catch (Exception ex) + { + OnRedisError(ex, cache); + throw; + } } public async Task SetManyAsync( @@ -120,9 +128,17 @@ public async Task SetManyAsync( { token.ThrowIfCancellationRequested(); - await ConnectAsync(token); + var cache = await ConnectAsync(token); - await Task.WhenAll(PipelineSetMany(items, options)); + try + { + await Task.WhenAll(PipelineSetMany(cache, items, options)); + } + catch (Exception ex) + { + OnRedisError(ex, cache); + throw; + } } public void RefreshMany( @@ -146,9 +162,17 @@ public void RemoveMany(IEnumerable keys) { keys = Check.NotNull(keys, nameof(keys)); - Connect(); + var cache = Connect(); - RedisDatabase.KeyDelete(keys.Select(key => (RedisKey)(Instance + key)).ToArray()); + try + { + cache.KeyDelete(keys.Select(key => InstancePrefix.Append(key)).ToArray()); + } + catch (Exception ex) + { + OnRedisError(ex, cache); + throw; + } } public async Task RemoveManyAsync(IEnumerable keys, CancellationToken token = default) @@ -156,33 +180,40 @@ public async Task RemoveManyAsync(IEnumerable keys, CancellationToken to keys = Check.NotNull(keys, nameof(keys)); token.ThrowIfCancellationRequested(); - await ConnectAsync(token); + var cache = await ConnectAsync(token); - await RedisDatabase.KeyDeleteAsync(keys.Select(key => (RedisKey)(Instance + key)).ToArray()); + try + { + await cache.KeyDeleteAsync(keys.Select(key => InstancePrefix.Append(key)).ToArray()); + } + catch (Exception ex) + { + OnRedisError(ex, cache); + throw; + } } protected virtual byte[]?[] GetAndRefreshMany( IEnumerable keys, bool getData) { - Connect(); + var cache = Connect(); - var keyArray = keys.Select(key => Instance + key).ToArray(); - RedisValue[][] results; + var keyArray = keys.Select(key => InstancePrefix.Append( key)).ToArray(); + byte[]?[] bytes; - if (getData) + try { - results = RedisDatabase.HashMemberGetMany(keyArray, AbsoluteExpirationKey, - SlidingExpirationKey, DataKey); + var results = cache.HashMemberGetMany(keyArray, GetHashFields(getData)); + + Task.WaitAll(PipelineRefreshManyAndOutData(cache, keyArray, results, out bytes)); } - else + catch (Exception ex) { - results = RedisDatabase.HashMemberGetMany(keyArray, AbsoluteExpirationKey, - SlidingExpirationKey); + OnRedisError(ex, cache); + throw; } - Task.WaitAll(PipelineRefreshManyAndOutData(keyArray, results, out var bytes)); - return bytes; } @@ -193,29 +224,28 @@ public async Task RemoveManyAsync(IEnumerable keys, CancellationToken to { token.ThrowIfCancellationRequested(); - await ConnectAsync(token); + var cache = await ConnectAsync(token); - var keyArray = keys.Select(key => Instance + key).ToArray(); - RedisValue[][] results; + var keyArray = keys.Select(key => InstancePrefix.Append(key)).ToArray(); + byte[]?[] bytes; - if (getData) + try { - results = await RedisDatabase.HashMemberGetManyAsync(keyArray, AbsoluteExpirationKey, - SlidingExpirationKey, DataKey); + var results = await cache.HashMemberGetManyAsync(keyArray, GetHashFields(getData)); + await Task.WhenAll(PipelineRefreshManyAndOutData(cache, keyArray, results, out bytes)); } - else + catch (Exception ex) { - results = await RedisDatabase.HashMemberGetManyAsync(keyArray, AbsoluteExpirationKey, - SlidingExpirationKey); + OnRedisError(ex, cache); + throw; } - - await Task.WhenAll(PipelineRefreshManyAndOutData(keyArray, results, out var bytes)); - + return bytes; } protected virtual Task[] PipelineRefreshManyAndOutData( - string[] keys, + IDatabase cache, + RedisKey[] keys, RedisValue[][] results, out byte[]?[] bytes) { @@ -242,7 +272,7 @@ protected virtual Task[] PipelineRefreshManyAndOutData( expr = sldExpr; } - tasks[i] = RedisDatabase.KeyExpireAsync(keys[i], expr); + tasks[i] = cache.KeyExpireAsync(keys[i], expr); } else { @@ -264,6 +294,7 @@ protected virtual Task[] PipelineRefreshManyAndOutData( } protected virtual Task[] PipelineSetMany( + IDatabase cache, IEnumerable> items, DistributedCacheEntryOptions options) { @@ -277,14 +308,13 @@ protected virtual Task[] PipelineSetMany( for (var i = 0; i < itemArray.Length; i++) { - tasks[i] = RedisDatabase.ScriptEvaluateAsync(GetSetScript(), new RedisKey[] { Instance + itemArray[i].Key }, - new RedisValue[] - { - absoluteExpiration?.Ticks ?? NotPresent, + tasks[i] = cache.ScriptEvaluateAsync(GetSetScript(), new RedisKey[] { InstancePrefix.Append(itemArray[i].Key) }, + [ + absoluteExpiration?.Ticks ?? NotPresent, options.SlidingExpiration?.Ticks ?? NotPresent, GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent, itemArray[i].Value - }); + ]); } return tasks; @@ -317,14 +347,21 @@ protected virtual void MapMetadata( { return (DateTimeOffset?)GetAbsoluteExpirationMethod.Invoke(null, new object[] { creationTime, options }); } - - private IDatabase? GetRedisDatabase() + + protected virtual void OnRedisError(Exception ex, IDatabase cache) { - return _redisDatabase ??= RedisDatabaseField.GetValue(this) as IDatabase; + OnRedisErrorMethod.Invoke(this, [ex, cache]); } private string GetSetScript() { return SetScriptField.GetValue(this)!.ToString()!; } + + private static RedisValue[] GetHashFields(bool getData) + { + return getData + ? HashMembersAbsoluteExpirationSlidingExpirationData + : HashMembersAbsoluteExpirationSlidingExpiration; + } } diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs index 5954c061215..840a77fb289 100644 --- a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs @@ -8,16 +8,15 @@ public static class AbpRedisExtensions { public static RedisValue[][] HashMemberGetMany( this IDatabase cache, - string[] keys, - params string[] members) + RedisKey[] keys, + RedisValue[] fields) { var tasks = new Task[keys.Length]; - var fields = members.Select(member => (RedisValue)member).ToArray(); var results = new RedisValue[keys.Length][]; for (var i = 0; i < keys.Length; i++) { - tasks[i] = cache.HashGetAsync((RedisKey)keys[i], fields); + tasks[i] = cache.HashGetAsync(keys[i], fields); } for (var i = 0; i < tasks.Length; i++) @@ -28,17 +27,16 @@ public static RedisValue[][] HashMemberGetMany( return results; } - public static async Task HashMemberGetManyAsync( + public async static Task HashMemberGetManyAsync( this IDatabase cache, - string[] keys, - params string[] members) + RedisKey[] keys, + RedisValue[] fields) { var tasks = new Task[keys.Length]; - var fields = members.Select(member => (RedisValue)member).ToArray(); for (var i = 0; i < keys.Length; i++) { - tasks[i] = cache.HashGetAsync((RedisKey)keys[i], fields); + tasks[i] = cache.HashGetAsync(keys[i], fields); } return await Task.WhenAll(tasks);