- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
个人觉得 ABP 分布式缓存模块有三个值得关注的核心点。首先是 AbpRedisCache 类继承了微软原生的 RedisCache,并 通过反射的方式获取RedisCache的私有方法对 RedisCache 进行扩展,实现了 ABP 分布式缓存中的批量操作方法.
为什么要这么做呢?因为基于 Redis 缓存的批量操作需要使用到 StackExchange.Redis 原生的SDK,而且在进行操作前总需要先进行 Redis 连接,而相应的方法和属性原生的 RedisCache 中都有,但是都是私有(private)的,在继承类中也无法使用,所以使用反射的方式提取出相应的方法和属性,以便在继承类中复用。这也是对于类功能进行继承扩展的时候的一种很有用的方式.
第二点是 ABP 缓存模块通过 IDistributedCache<CacheItem, CacheKey> 接口扩展了原生的 IDistributedCache 接口的功能,而在具体实现上是将原有的 IDistributedCache 服务注入进行复用相应功能的,并在前后增加额外的逻辑对功能进行扩展增强,实际上就是适配器模式.
而最常使用的 IDistributedCache<CacheItem> 接口,以前其实现类是直接继承 DistributedCache<CacheItem, CacheKey> 的,现在改成了直接用适配器模式.
最后一个是缓存的事务性,通过工作单元使缓存和其他事务操作保持原子性,避免缓存已经更新而其他事务失败回滚导致数据不一致的问题。实际上就是先不真正地更新缓存,而是将缓存数据通过字典保存在工作单元中,保证一个工作单元内拿到的缓存数据是最新的,同时注册工作单元提交事件,在工作单元正在提交成功的时候才执行真正更新缓存的逻辑.
工作单元相关的内容就后面再在专门的章节讲吧,这部分的内容比较复杂,一时半会比较难讲清.
ABP 框架扩展的 IDistributedCache 泛型接口在内部帮我们处理实例对象进行序列化/反序列化以及转码为 byte 数组的问题,大大提升了我们使用分布式缓存的方便性,但也存在一些问题.
IDistributedCache<CacheItem>
接口进行扩展。内部实现基本一致,主要就是将基于类的泛型,改成基于方法的泛型,并且提供自己的 IDistributedCacheKeyNormalizer 实现类,将缓存键的设置规则交给了缓存存取时进行设置。 代码如下:
public interface IWantDistributedCache
{
/// <summary>
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item, or null.</returns>
TCacheItem Get<TCacheItem, TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false
) where TCacheItem : class;
/// <summary>
/// Gets multiple cache items with the given keys.
///
/// The returned list contains exactly the same count of items specified in the given keys.
/// An item in the return list can not be null, but an item in the list has null value
/// if the related key not found in the cache.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>List of cache items.</returns>
KeyValuePair<TCacheKey, TCacheItem>[] GetMany<TCacheItem, TCacheKey>(
IEnumerable<TCacheKey> keys,
string cacheName,
bool? hideErrors = null,
bool considerUow = false
) where TCacheItem : class;
/// <summary>
/// Gets multiple cache items with the given keys.
///
/// The returned list contains exactly the same count of items specified in the given keys.
/// An item in the return list can not be null, but an item in the list has null value
/// if the related key not found in the cache.
///
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>List of cache items.</returns>
Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync<TCacheItem, TCacheKey>(
IEnumerable<TCacheKey> keys,
string cacheName,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
) where TCacheItem : class;
/// <summary>
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns>
Task<TCacheItem> GetAsync<TCacheItem, TCacheKey>(
[NotNull] TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
) where TCacheItem : class;
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item.</returns>
TCacheItem GetOrAdd<TCacheItem, TCacheKey>(
TCacheKey key,
Func<TCacheItem> factory,
string cacheName,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false
) where TCacheItem : class;
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item.</returns>
Task<TCacheItem> GetOrAddAsync<TCacheItem, TCacheKey>(
[NotNull] TCacheKey key,
Func<Task<TCacheItem>> factory,
string cacheName,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
) where TCacheItem : class;
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
void Set<TCacheItem, TCacheKey>(
TCacheKey key,
TCacheItem value,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false
) where TCacheItem : class;
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task SetAsync<TCacheItem, TCacheKey>(
[NotNull] TCacheKey key,
[NotNull] TCacheItem value,
string cacheName,
[CanBeNull] DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
) where TCacheItem : class;
/// <summary>
/// Sets multiple cache items.
/// Based on the implementation, this can be more efficient than setting multiple items individually.
/// </summary>
/// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
void SetMany<TCacheItem, TCacheKey>(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false
) where TCacheItem : class;
/// <summary>
/// Sets multiple cache items.
/// Based on the implementation, this can be more efficient than setting multiple items individually.
/// </summary>
/// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task SetManyAsync<TCacheItem, TCacheKey>(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
) where TCacheItem : class;
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
void Refresh<TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null
);
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task RefreshAsync<TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
CancellationToken token = default
);
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
void Remove<TCacheItem, TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false
) where TCacheItem : class;
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task RemoveAsync<TCacheItem, TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
) where TCacheItem : class;
}
public class WantDistributedCache : IWantDistributedCache
{
public const string UowCacheName = "WantDistributedCache";
public ILogger<WantDistributedCache> Logger { get; set; }
protected string CacheName { get; set; }
protected bool IgnoreMultiTenancy { get; set; }
protected IDistributedCache Cache { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IDistributedCacheSerializer Serializer { get; }
protected IDistributedCacheKeyNormalizer KeyNormalizer { get; }
protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected SemaphoreSlim SyncSemaphore { get; }
protected DistributedCacheEntryOptions DefaultCacheOptions;
private readonly AbpDistributedCacheOptions _distributedCacheOption;
public WantDistributedCache(
IOptions<AbpDistributedCacheOptions> distributedCacheOption,
IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory,
IUnitOfWorkManager unitOfWorkManager)
{
_distributedCacheOption = distributedCacheOption.Value;
Cache = cache;
CancellationTokenProvider = cancellationTokenProvider;
Logger = NullLogger<WantDistributedCache>.Instance;
Serializer = serializer;
KeyNormalizer = keyNormalizer;
ServiceScopeFactory = serviceScopeFactory;
UnitOfWorkManager = unitOfWorkManager;
SyncSemaphore = new SemaphoreSlim(1, 1);
SetDefaultOptions();
}
protected virtual string NormalizeKey<TCacheKey>(TCacheKey key, string cacheName)
{
return KeyNormalizer.NormalizeKey(
new DistributedCacheKeyNormalizeArgs(
key.ToString(),
cacheName,
IgnoreMultiTenancy
)
);
}
protected virtual DistributedCacheEntryOptions GetDefaultCacheEntryOptions()
{
foreach (var configure in _distributedCacheOption.CacheConfigurators)
{
var options = configure.Invoke(CacheName);
if (options != null)
{
return options;
}
}
return _distributedCacheOption.GlobalCacheEntryOptions;
}
protected virtual void SetDefaultOptions()
{
//CacheName = CacheNameAttribute.GetCacheName(typeof(TCacheItem));
////IgnoreMultiTenancy
//IgnoreMultiTenancy = typeof(TCacheItem).IsDefined(typeof(IgnoreMultiTenancyAttribute), true);
//Configure default cache entry options
DefaultCacheOptions = GetDefaultCacheEntryOptions();
}
/// <summary>
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item, or null.</returns>
public virtual TCacheItem Get<TCacheItem, TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
if (ShouldConsiderUow(considerUow))
{
var value = GetUnitOfWorkCache<TCacheItem, TCacheKey>().GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
return value;
}
}
byte[] cachedBytes;
try
{
cachedBytes = Cache.Get(NormalizeKey(key, cacheName));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return null;
}
throw;
}
return ToCacheItem<TCacheItem>(cachedBytes);
}
public virtual KeyValuePair<TCacheKey, TCacheItem>[] GetMany<TCacheItem, TCacheKey>(
IEnumerable<TCacheKey> keys,
string cacheName,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
return GetManyFallback<TCacheItem, TCacheKey>(
keyArray,
cacheName,
hideErrors,
considerUow
);
}
var notCachedKeys = new List<TCacheKey>();
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
foreach (var key in keyArray)
{
var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
cachedValues.Add(new KeyValuePair<TCacheKey, TCacheItem>(key, value));
}
}
notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList();
if (!notCachedKeys.Any())
{
return cachedValues.ToArray();
}
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes;
var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;
try
{
cachedBytes = cacheSupportsMultipleItems.GetMany(readKeys.Select(key => NormalizeKey(key, cacheName)));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return ToCacheItemsWithDefaultValues<TCacheItem, TCacheKey>(keyArray);
}
throw;
}
return cachedValues.Concat(ToCacheItems<TCacheItem, TCacheKey>(cachedBytes, readKeys)).ToArray();
}
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] GetManyFallback<TCacheItem, TCacheKey>(
TCacheKey[] keys,
string cacheName,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
return keys
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>(
key,
Get<TCacheItem, TCacheKey>(key, cacheName, false, considerUow)
)
).ToArray();
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return ToCacheItemsWithDefaultValues<TCacheItem, TCacheKey>(keys);
}
throw;
}
}
public virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync<TCacheItem, TCacheKey>(
IEnumerable<TCacheKey> keys,
string cacheName,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
return await GetManyFallbackAsync<TCacheItem, TCacheKey>(
keyArray,
cacheName,
hideErrors,
considerUow,
token
);
}
var notCachedKeys = new List<TCacheKey>();
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
foreach (var key in keyArray)
{
var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
cachedValues.Add(new KeyValuePair<TCacheKey, TCacheItem>(key, value));
}
}
notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList();
if (!notCachedKeys.Any())
{
return cachedValues.ToArray();
}
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes;
var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;
try
{
cachedBytes = await cacheSupportsMultipleItems.GetManyAsync(
readKeys.Select(key => NormalizeKey(key, cacheName)),
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return ToCacheItemsWithDefaultValues<TCacheItem, TCacheKey>(keyArray);
}
throw;
}
return cachedValues.Concat(ToCacheItems<TCacheItem, TCacheKey>(cachedBytes, readKeys)).ToArray();
}
protected virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyFallbackAsync<TCacheItem, TCacheKey>(
TCacheKey[] keys,
string cacheName,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
var result = new List<KeyValuePair<TCacheKey, TCacheItem>>();
foreach (var key in keys)
{
result.Add(new KeyValuePair<TCacheKey, TCacheItem>(
key,
await GetAsync<TCacheItem, TCacheKey>(key, cacheName, false, considerUow, token: token))
);
}
return result.ToArray();
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return ToCacheItemsWithDefaultValues<TCacheItem, TCacheKey>(keys);
}
throw;
}
}
/// <summary>
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns>
public virtual async Task<TCacheItem> GetAsync<TCacheItem, TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
if (ShouldConsiderUow(considerUow))
{
var value = GetUnitOfWorkCache<TCacheItem, TCacheKey>().GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
return value;
}
}
byte[] cachedBytes;
try
{
cachedBytes = await Cache.GetAsync(
NormalizeKey(key, cacheName),
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return null;
}
throw;
}
if (cachedBytes == null)
{
return null;
}
return Serializer.Deserialize<TCacheItem>(cachedBytes);
}
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item.</returns>
public virtual TCacheItem GetOrAdd<TCacheItem, TCacheKey>(
TCacheKey key,
Func<TCacheItem> factory,
string cacheName,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
var value = Get<TCacheItem, TCacheKey>(key, cacheName, hideErrors, considerUow);
if (value != null)
{
return value;
}
using (SyncSemaphore.Lock())
{
value = Get<TCacheItem, TCacheKey>(key, cacheName, hideErrors, considerUow);
if (value != null)
{
return value;
}
value = factory();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
if (uowCache.TryGetValue(key, out var item))
{
item.SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
}
Set(key, value, cacheName, optionsFactory?.Invoke(), hideErrors, considerUow);
}
return value;
}
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item.</returns>
public virtual async Task<TCacheItem> GetOrAddAsync<TCacheItem, TCacheKey>(
TCacheKey key,
Func<Task<TCacheItem>> factory,
string cacheName,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
token = CancellationTokenProvider.FallbackToProvider(token);
var value = await GetAsync<TCacheItem, TCacheKey>(key, cacheName, hideErrors, considerUow, token);
if (value != null)
{
return value;
}
using (await SyncSemaphore.LockAsync(token))
{
value = await GetAsync<TCacheItem, TCacheKey>(key, cacheName, hideErrors, considerUow, token);
if (value != null)
{
return value;
}
value = await factory();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
if (uowCache.TryGetValue(key, out var item))
{
item.SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
}
await SetAsync(key, value, cacheName, optionsFactory?.Invoke(), hideErrors, considerUow, token);
}
return value;
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
public virtual void Set<TCacheItem, TCacheKey>(
TCacheKey key,
TCacheItem value,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
void SetRealCache()
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Set(
NormalizeKey(key, cacheName),
Serializer.Serialize(value),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
if (uowCache.TryGetValue(key, out _))
{
uowCache[key].SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
SetRealCache();
return Task.CompletedTask;
});
}
else
{
SetRealCache();
}
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
public virtual async Task SetAsync<TCacheItem, TCacheKey>(
TCacheKey key,
TCacheItem value,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
async Task SetRealCache()
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await Cache.SetAsync(
NormalizeKey(key, cacheName),
Serializer.Serialize(value),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
if (uowCache.TryGetValue(key, out _))
{
uowCache[key].SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(SetRealCache);
}
else
{
await SetRealCache();
}
}
public void SetMany<TCacheItem, TCacheKey>(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
var itemsArray = items.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
SetManyFallback(
itemsArray,
cacheName,
options,
hideErrors,
considerUow
);
return;
}
void SetRealCache()
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
cacheSupportsMultipleItems.SetMany(
ToRawCacheItems(itemsArray, cacheName),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
foreach (var pair in itemsArray)
{
if (uowCache.TryGetValue(pair.Key, out _))
{
uowCache[pair.Key].SetValue(pair.Value);
}
else
{
uowCache.Add(pair.Key, new UnitOfWorkCacheItem<TCacheItem>(pair.Value));
}
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
SetRealCache();
return Task.CompletedTask;
});
}
else
{
SetRealCache();
}
}
protected virtual void SetManyFallback<TCacheItem, TCacheKey>(
KeyValuePair<TCacheKey, TCacheItem>[] items,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
foreach (var item in items)
{
Set(
item.Key,
item.Value,
cacheName,
options,
false,
considerUow
);
}
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
public virtual async Task SetManyAsync<TCacheItem, TCacheKey>(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
var itemsArray = items.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
await SetManyFallbackAsync(
itemsArray,
cacheName,
options,
hideErrors,
considerUow,
token
);
return;
}
async Task SetRealCache()
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await cacheSupportsMultipleItems.SetManyAsync(
ToRawCacheItems(itemsArray, cacheName),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
foreach (var pair in itemsArray)
{
if (uowCache.TryGetValue(pair.Key, out _))
{
uowCache[pair.Key].SetValue(pair.Value);
}
else
{
uowCache.Add(pair.Key, new UnitOfWorkCacheItem<TCacheItem>(pair.Value));
}
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(SetRealCache);
}
else
{
await SetRealCache();
}
}
protected virtual async Task SetManyFallbackAsync<TCacheItem, TCacheKey>(
KeyValuePair<TCacheKey, TCacheItem>[] items,
string cacheName,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
foreach (var item in items)
{
await SetAsync(
item.Key,
item.Value,
cacheName,
options,
false,
considerUow,
token: token
);
}
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Refresh<TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Refresh(NormalizeKey(key, cacheName));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
public virtual async Task RefreshAsync<TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await Cache.RefreshAsync(NormalizeKey(key, cacheName), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Remove<TCacheItem, TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false) where TCacheItem : class
{
void RemoveRealCache()
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Remove(NormalizeKey(key, cacheName));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
if (uowCache.TryGetValue(key, out _))
{
uowCache[key].RemoveValue();
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
RemoveRealCache();
return Task.CompletedTask;
});
}
else
{
RemoveRealCache();
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
public virtual async Task RemoveAsync<TCacheItem, TCacheKey>(
TCacheKey key,
string cacheName,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default) where TCacheItem : class
{
async Task RemoveRealCache()
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await Cache.RemoveAsync(NormalizeKey(key, cacheName), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache<TCacheItem, TCacheKey>();
if (uowCache.TryGetValue(key, out _))
{
uowCache[key].RemoveValue();
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(RemoveRealCache);
}
else
{
await RemoveRealCache();
}
}
protected virtual void HandleException(Exception ex)
{
AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
}
protected virtual async Task HandleExceptionAsync(Exception ex)
{
Logger.LogException(ex, LogLevel.Warning);
using (var scope = ServiceScopeFactory.CreateScope())
{
await scope.ServiceProvider
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning));
}
}
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItems<TCacheItem, TCacheKey>(byte[][] itemBytes, TCacheKey[] itemKeys) where TCacheItem: class
{
if (itemBytes.Length != itemKeys.Length)
{
throw new AbpException("count of the item bytes should be same with the count of the given keys");
}
var result = new List<KeyValuePair<TCacheKey, TCacheItem>>();
for (int i = 0; i < itemKeys.Length; i++)
{
result.Add(
new KeyValuePair<TCacheKey, TCacheItem>(
itemKeys[i],
ToCacheItem<TCacheItem>(itemBytes[i])
)
);
}
return result.ToArray();
}
[CanBeNull]
protected virtual TCacheItem ToCacheItem<TCacheItem>([CanBeNull] byte[] bytes) where TCacheItem : class
{
if (bytes == null)
{
return null;
}
return Serializer.Deserialize<TCacheItem>(bytes);
}
protected virtual KeyValuePair<string, byte[]>[] ToRawCacheItems<TCacheItem, TCacheKey>(KeyValuePair<TCacheKey, TCacheItem>[] items, string cacheName)
{
return items
.Select(i => new KeyValuePair<string, byte[]>(
NormalizeKey(i.Key, cacheName),
Serializer.Serialize(i.Value)
)
).ToArray();
}
private static KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItemsWithDefaultValues<TCacheItem, TCacheKey>(TCacheKey[] keys)
{
return keys
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>(key, default))
.ToArray();
}
protected virtual bool ShouldConsiderUow(bool considerUow)
{
return considerUow && UnitOfWorkManager.Current != null;
}
protected virtual string GetUnitOfWorkCacheKey()
{
return UowCacheName + CacheName;
}
protected virtual Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>> GetUnitOfWorkCache<TCacheItem, TCacheKey>() where TCacheItem : class
{
if (UnitOfWorkManager.Current == null)
{
throw new AbpException($"There is no active UOW.");
}
return UnitOfWorkManager.Current.GetOrAddItem(GetUnitOfWorkCacheKey(),
key => new Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>>());
}
}
[Dependency(ReplaceServices = true)]
public class WantDistributedCacheKeyNormalizer : IDistributedCacheKeyNormalizer, ITransientDependency
{
protected AbpDistributedCacheOptions DistributedCacheOptions { get; }
public SuncereDistributedCacheKeyNormalizer(
IOptions<AbpDistributedCacheOptions> distributedCacheOptions)
{
DistributedCacheOptions = distributedCacheOptions.Value;
}
public virtual string NormalizeKey(DistributedCacheKeyNormalizeArgs args)
{
// 缓存格式: a:appname,c:cachename,k:key
var normalizedKey = $"a:{DistributedCacheOptions.KeyPrefix},c:{args.CacheName},k:{args.Key}";
return normalizedKey;
}
}
[DependsOn(typeof(AbpCachingModule))]
public class WantAbpCacheModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//注入缓存类
context.Services.AddSingleton(typeof(ISuncereDistributedCache), typeof(SuncereDistributedCache));
}
}
参考文章: ABP 官方文档 - 缓存 。
ABP 系列总结: 目录: ABP 系列总结 上一篇: ABP - 缓存模块(1) 。
最后此篇关于ABP-缓存模块(2)的文章就讲到这里了,如果你想了解更多关于ABP-缓存模块(2)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我阅读了有关 JSR 107 缓存 (JCache) 的内容。 我很困惑:据我所知,每个 CPU 都管理其缓存内存(无需操作系统的任何帮助)。 那么,为什么我们需要 Java 缓存处理程序? (如果C
好吧,我是 jQuery 的新手。我一直在这里和那里搞乱一点点并习惯它。我终于明白了(它并不像某些人想象的那么难)。因此,鉴于此链接:http://jqueryui.com/sortable/#dis
我正在使用 Struts 2 和 Hibernate。我有一个简单的表,其中包含一个日期字段,用于存储有关何时发生特定操作的信息。这个日期值显示在我的 jsp 中。 我遇到的问题是hibernate更
我有点不确定这里发生了什么,但是我试图解释正在发生的事情,也许一旦我弄清楚我到底在问什么,就可能写一个更好的问题。 我刚刚安装了Varnish,对于我的请求时间来说似乎很棒。这是一个Magneto 2
解决 Project Euler 的问题后,我在论坛中发现了以下 Haskell 代码: fillRow115 minLength = cache where cache = ((map fill
我正试图找到一种方法来为我网络上的每台计算机缓存或存储某些 python 包。我看过以下解决方案: pypicache但它不再被积极开发,作者推荐 devpi,请参见此处:https://bitbuc
我想到的一个问题是可以从一开始就缓存网络套接字吗?在我的拓扑中,我在通过双 ISP 连接连接到互联网的 HAProxy 服务器后面有 2 个 Apache 服务器(带有 Google PageSpee
我很难说出不同缓存区域 (OS) 之间的区别。我想简要解释一下磁盘\缓冲区\交换\页面缓存。他们住在哪里?它们之间的主要区别是什么? 据我了解,页面缓存是主内存的一部分,用于存储从 I/O 设备获取的
1.题目 请你为最不经常使用(LFU)缓存算法设计并实现数据结构。 实现 LFUCache 类: LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象 in
1.题目 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: ① LRUCache(int capacity) 以正整数作为容量 capacity
我想在访问该 View 时关闭某些页面的缓存。它适用于简单查询模型对象的页面。 好像什么时候 'django.middleware.cache.FetchFromCacheMiddleware', 启
documents为 ExePackage element state Cache属性的目的是 Whether to cache the package. The default is "yes".
我知道 docker 用图层存储每个图像。如果我在一台开发服务器上有多个用户,并且每个人都在运行相同的 Dockerfile,但将镜像存储为 user1_myapp . user2 将其存储为 use
在 Codeigniter 中没有出现缓存问题几年后,我发现了一个问题。我在其他地方看到过该问题,但没有适合我的解决方案。 例如,如果我在 View 中更改一些纯 html 文本并上传新文件并按 F5
我在 Janusgraph 文档中阅读了有关 Janusgraph Cache 的内容。关于事务缓存,我几乎没有怀疑。我在我的应用程序中使用嵌入式 janusgrah 服务器。 如果我只对例如进行读取
我想知道是否有来自终端的任何命令可以用来匹配 Android Studio 中执行文件>使缓存无效/重新启动的使用。 谢谢! 最佳答案 According to a JetBrains employe
我想制作一个 python 装饰器来内存函数。例如,如果 @memoization_decorator def add(a, b, negative=False): print "Com
我经常在 jQuery 事件处理程序中使用 $(this) 并且从不缓存它。如果我愿意的话 var $this = $(this); 并且将使用变量而不是构造函数,我的代码会获得任何显着的额外性能吗?
是的,我要说实话,我不知道varnish vcl,我可以解决一些基本问题,但是我不太清楚,这就是为什么我遇到问题了。 我正在尝试通过http请求设置缓存禁止,但是该请求不能通过DNS而是通过 Varn
在 WP 站点上加载约 4000 个并发用户时遇到此问题。 这是我的配置: F5 负载均衡器 ---> Varnish 4,8 核,32 Gb RAM ---> 9 个后端,4 个核,每个 16 RA
我是一名优秀的程序员,十分优秀!