.net系统缓存
.net系统缓存封装设计
缓存管理的接口ICache
,可以用来保存键值对数据,设置缓存过期时间等功能,接口设计如下:
public interface ICache
{
bool IsInitialized { get; }
Task<Result> InitAsync(string connStr);
Task<T> GetAsync<T>(string key, Func<T> defaultFunc = null, bool setDefault = false);
Task<bool> SetAsync<T>(string key, T value, int expiredSeconds = 0);
Task<bool> RemoveAsync(string key);
Task<bool> ExistsAsync(string key);
Task<long> SAddAsync<T>(string key, params T[] members);
Task<long> SCountAsync(string key);
Task<T[]> SMembersAsync<T>(string key);
Task<bool> SIsMemberAsync<T>(string key, T member);
Task<long> SRemoveAsync<T>(string key, params T[] members);
/// <summary>
/// 设置指定Key的过期时间
/// </summary>
/// <param name="key">The key.</param>
/// <param name="expiredSeconds">The expired seconds.</param>
/// <returns>Task<System.Boolean>.</returns>
Task<bool> SetExpireTimeAsync(string key, int expiredSeconds);
}
服务端缓存采用Redis
实现,具体实现如下:
internal class RedisCache : ICache
{
private IRedisClient _client;
private void CheckValid()
{
if (_client == null)
throw new InvalidOperationException("RedisClient未初始化!");
}
public bool IsInitialized => _client != null;
public async Task<Result> InitAsync(string connStr)
{
try
{
if (_client != null)
return Result.Ok;
//connStr = "data source=127.0.0.1:6379;initial catalog=1";
_client = await RedisClient.ConnectAsync(connStr, new RedisJsonSerializer());
return Result.Ok;
}
catch (Exception e)
{
return new Result(e, "初始化Redis客户端失败!");
}
}
public void ClearCache()
{
_client?.FlushDbAsync().Wait();
}
public async Task<T> GetAsync<T>(string key, Func<T> defaultFunc = null, bool setDefault = false)
{
CheckValid();
try
{
var bulk = await _client.GetAsync(key);
if (!bulk.IsNull)
return bulk.As<T>();
}
catch
{
//ignore
}
if (defaultFunc == null)
throw new KeyNotFoundException($"Redis中值为{key}的键不存在,或值不是{typeof(T)}");
var value = defaultFunc();
if (setDefault)
await SetAsync(key, value, 0);
return value;
}
public async Task<bool> SetAsync<T>(string key, T value, int expiredSeconds)
{
CheckValid();
string result;
if (expiredSeconds > 0)
result = await _client.SetExAsync(key, expiredSeconds, value);
else
result = await _client.SetAsync(key, value);
return result == "OK";
}
public async Task<bool> RemoveAsync(string key)
{
CheckValid();
var r = await _client.DelAsync(key);
return r == 1;
}
public async Task<bool> ExistsAsync(string key)
{
CheckValid();
var r = await _client.ExistsAsync(key);
return r == 1;
}
public async Task<long> SAddAsync<T>(string key, params T[] members)
{
CheckValid();
var r = await _client.SAddAsync(key, members);
return r;
}
public async Task<long> SCountAsync(string key)
{
CheckValid();
var r = await _client.SCardAsync(key);
return r;
}
public async Task<T[]> SMembersAsync<T>(string key)
{
CheckValid();
var r = await _client.SMembersAsync(key);
return r.AsArray<T>();
}
public async Task<bool> SIsMemberAsync<T>(string key, T member)
{
CheckValid();
var r = await _client.SIsMemberAsync(key, member);
return r == 1;
}
public async Task<long> SRemoveAsync<T>(string key, params T[] members)
{
CheckValid();
var r = await _client.SRemAsync(key, members);
return r;
}
public async Task<long> HCountAsync(string key)
{
CheckValid();
return await _client.HLenAsync(key);
}
public async Task<T> HGetAsync<T>(string key, string field, Func<T> defaultFunc = null)
{
CheckValid();
var r = await _client.HExistsAsync(key, field);
if (r == 1)
{
try
{
var bulk = await _client.HGetAsync(key, field);
if (!bulk.IsNull)
return bulk.As<T>();
}
catch
{
//ignore
}
}
if (defaultFunc == null)
throw new KeyNotFoundException($"Redis中值为{key}-{field}的键不存在,或值不是{typeof(T)}");
return defaultFunc();
}
public async Task HSetAsync<T>(string key, string field, T value)
{
CheckValid();
await _client.HSetAsync(key, field, value);
}
public async Task<bool> HRemoveAsync(string key, string field)
{
CheckValid();
var count = await _client.HDelAsync(key, field);
return count == 1;
}
public async Task HRemoveAsync(string key, string[] fields)
{
CheckValid();
await _client.HDelAsync(key, fields);
}
public async Task<bool> HExistsAsync(string key, string field)
{
CheckValid();
return await _client.HExistsAsync(key, field) == 1;
}
public async Task<string[]> HKeysAsync(string key)
{
CheckValid();
var bulk = await _client.HKeysAsync(key);
return bulk.IsNull ? new string[0] : bulk.AsArray<string>();
}
public async Task<KeyValuePair<string, T>[]> HAllAsync<T>(string key)
{
CheckValid();
var bulk = await _client.HGetAllAsync(key);
if (bulk.IsNull)
return new KeyValuePair<string, T>[0];
var values = bulk.AsArray<string>();
var results = new KeyValuePair<string, T>[values.Length / 2];
for (var i = 0; i < values.Length; i += 2)
{
var k = values[i];
var v = values[i + 1].As<T>();
results[i / 2] = new KeyValuePair<string, T>(k, v);
}
return results;
}
public async Task<bool> SetExpireTimeAsync(string key, int expiredSeconds)
{
CheckValid();
if (expiredSeconds < 0)
return false;
var r = await _client.ExpireAsync(key, expiredSeconds);
return r == 1;
}
}
CS及MS端的缓存采用Akavache
实现,具体实现如下:
internal partial class Cache : ICache
{
public bool IsInitialized => true;
public Task<Result> InitAsync(string connStr)
{
return Task.FromResult(Result.Ok);
}
public async Task<T> GetAsync<T>(string key, Func<T> defaultFunc = null, bool setDefault = false)
{
try
{
return await BlobCache.LocalMachine.GetObject<T>(key);
}
catch (Exception)
{
if (defaultFunc == null)
throw;
}
var value = defaultFunc();
if (setDefault)
await SetAsync(key, value);
return value;
}
public async Task<bool> SetAsync<T>(string key, T value, int expiredSeconds = 0)
{
try
{
DateTimeOffset? offset = null;
if (expiredSeconds > 0)
offset = new DateTimeOffset(DateTime.Now.AddSeconds(expiredSeconds));
await BlobCache.LocalMachine.InsertObject(key, value, offset);
return true;
}
catch
{
return false;
}
}
public async Task<bool> RemoveAsync(string key)
{
try
{
await BlobCache.LocalMachine.Invalidate(key);
return true;
}
catch
{
return false;
}
}
public async Task<bool> ExistsAsync(string key)
{
try
{
await BlobCache.LocalMachine.Get(key);
return true;
}
catch (Exception)
{
return false;
}
}
public async Task<long> SAddAsync<T>(string key, params T[] members)
{
var set = await GetAsync(key, () => new HashSet<string>(), true);
var c1 = set.Count;
set.AddRange(members.Select(t => JsonConvert.SerializeObject(t)));
if (await SetAsync(key, set))
return set.Count - c1;
return -1;
}
public async Task<long> SCountAsync(string key)
{
try
{
var col = await GetAsync<IEnumerable<object>>(key);
return col.Count();
}
catch (Exception)
{
return 0;
}
}
public async Task<T[]> SMembersAsync<T>(string key)
{
var set = await GetAsync(key, () => new HashSet<string>());
return set.Select(JsonConvert.DeserializeObject<T>).ToArray();
}
public async Task<bool> SIsMemberAsync<T>(string key, T member)
{
var set = await GetAsync(key, () => new HashSet<string>());
var mem = JsonConvert.SerializeObject(member);
return set.Contains(mem);
}
public async Task<long> SRemoveAsync<T>(string key, params T[] members)
{
var set = await GetAsync(key, () => new HashSet<string>());
if (set.Count == 0)
return 0;
var count = 0;
foreach (var member in members)
{
var mem = JsonConvert.SerializeObject(member);
if (set.Remove(mem))
count++;
}
if (await SetAsync(key, set))
return count;
return -1;
}
public async Task<bool> SetExpireTimeAsync(string key, int expiredSeconds)
{
if (expiredSeconds <= 0)
return false;
try
{
var data = await BlobCache.LocalMachine.Get(key);
DateTimeOffset? offset = null;
if (expiredSeconds > 0)
offset = new DateTimeOffset(DateTime.Now.AddSeconds(expiredSeconds));
await BlobCache.LocalMachine.Insert(key, data, offset);
return true;
}
catch (Exception e)
{
Mg.Get<IMgLog>().Error($"设置缓存过期时间失败:key={key},expiredSeconds={expiredSeconds}", e);
return false;
}
}
}