Files
zszq-trs/Framework/YLErp.Cache/YLRedisCache.cs
T

261 lines
8.8 KiB
C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StackExchange.Redis;
using StackExchange.Redis.KeyspaceIsolation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace YLErp.Cache
{
public class YLRedisCache : IYLCache
{
private readonly IOptions<CacheConfig> _cacheConfig;
private readonly ConnectionMultiplexer _redisConnector;
private readonly IServer redisServer;
private IDatabase db;
public YLRedisCache(IOptions<CacheConfig> cacheConfig)
{
_cacheConfig = cacheConfig;
if (_cacheConfig.Value.Enable)
{
var redisOptions= ConfigurationOptions.Parse(cacheConfig.Value.ConnectionString);
_redisConnector = ConnectionMultiplexer.Connect(redisOptions);
redisServer = _redisConnector.GetServer(redisOptions.EndPoints.FirstOrDefault());
db = _redisConnector.GetDatabase(cacheConfig.Value.DataBaseNum);
//db.WithKeyPrefix(GenerateKey(""));
}
}
/// <summary>
/// 缓存是否可用
/// </summary>
/// <returns></returns>
public bool CacheEnable()
{
return _cacheConfig.Value.Enable;
}
public bool DeleteKey(string key)
{
return db.KeyDelete(key);
}
public bool StringSet(string key, string value)
{
return db.StringSet(GenerateKey(key), value);
}
public bool HashSet<T>(string key, string hashKey, T value)
{
return db.HashSet(key, hashKey, JsonConvert.SerializeObject(value));
}
public string StringGet(string key)
{
return db.StringGet(GenerateKey(key));
}
public bool StringSet<T>(string key, object value)
{
return StringSet(key, JsonConvert.SerializeObject(value));
}
public T StringGet<T>(string key) where T : class
{
var value = StringGet(key);
if (string.IsNullOrEmpty(value))
{
return null;
}
return JsonConvert.DeserializeObject<T>(value);
}
public T StringGetWithNoPrefix<T>(string key) where T : class
{
var value = db.StringGet(key);
if (string.IsNullOrEmpty(value))
{
return null;
}
return JsonConvert.DeserializeObject<T>(value);
}
#region Batch Operate
private async Task<List<RedisValue>> SerializeObject<T>(List<T> objs)
{
return await Task.Factory.StartNew(() =>
{
List<RedisValue> redisValues = new List<RedisValue>();
foreach (T obj in objs)
{
redisValues.Add(JsonConvert.SerializeObject(obj));
}
return redisValues;
});
}
private async Task<RedisValue> SerializeObject<T>(T obj)
{
return await Task.Factory.StartNew(() =>
{
return JsonConvert.SerializeObject(obj);
});
}
/// <summary>
/// 批量增加
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="preKey"></param>
/// <param name="setSize"></param>
/// <param name="values"></param>
public async Task BatchAdd<T>(string preKey,List<T> values, int setSize = 1000)
{
//Stopwatch stopwatch1= Stopwatch.StartNew();
//List<Task> tasktest=new List<Task>();
//foreach (var obj in values)
//{
// tasktest.Add(SerializeObject(obj));
//}
//await Task.WhenAll(tasktest);
//stopwatch1.Stop();
//Console.WriteLine($"测试序列化耗时:{stopwatch1.ElapsedMilliseconds}");
//for (var i = 0; i < values.Count; i++)
//{
// int page = i / setSize;
// if (!dicRedisValue.ContainsKey(page))
// {
// dicRedisValue.Add(page, new List<RedisValue>());
// }
// dicRedisValue[page].Add(JsonConvert.SerializeObject(values[i]));
//}
//var batch = db.CreateBatch();
//List<Task> tasks = new List<Task>();
//for (int i = 0; i < values.Count; i++)
//{
// int page = i / setSize;
// var key = GenerateKey($"{preKey}:{page}");
// tasks.Add(batch.SetAddAsync(key, JsonConvert.SerializeObject(values[i])));
//}
Stopwatch stopwatch = Stopwatch.StartNew();
// 数据分组
Dictionary<int, Task<List<RedisValue>>> dicRedisValue = new Dictionary<int, Task<List<RedisValue>>>();
var valueCount = values.Count;
var pageCount = valueCount % setSize > 0 ? (valueCount / setSize) + 1 : (valueCount / setSize);
var page = 0;
while (page <=pageCount)
{
var takeDataCount = page * setSize > valueCount ? valueCount - (page - 1) * setSize : setSize;
var datas = values.Skip(page * setSize).Take(takeDataCount);
dicRedisValue.Add(page, SerializeObject(datas.ToList()));
page++;
}
await Task.WhenAll(dicRedisValue.Values);
stopwatch.Stop();
Console.WriteLine($"Redis批量保存序列化耗时:{stopwatch.ElapsedMilliseconds}");
// 保存数据
stopwatch.Restart();
List<Task> tasks = new List<Task>();
// 每组对应一个Set
var batch = db.CreateBatch();
foreach (int itemKey in dicRedisValue.Keys)
{
var key = GenerateKey($"{preKey}:{itemKey}");
var value = dicRedisValue[itemKey].Result.ToArray();
tasks.Add(batch.SetAddAsync(key, value));
}
batch.Execute();
await Task.WhenAll(tasks);
stopwatch.Stop();
Console.WriteLine($"redis 添加执行耗时:{stopwatch.ElapsedMilliseconds}");
}
/// <summary>
/// 批量删除
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="keys"></param>
public bool BatchDelete(string pattern)
{
var endpoints = _redisConnector.GetEndPoints(); // 获取所有节点
var patterns = GenerateKey(pattern);
foreach (var endpoint in endpoints)
{
var server = _redisConnector.GetServer(endpoint);
var keys = server.Keys(db.Database, pattern: patterns);
// 逐键删除(规避跨槽位)
foreach (var key in keys)
{
db.KeyDelete(key);
}
}
return true;
}
/// <summary>
/// 批量查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="pattern"></param>
/// <returns></returns>
public List<T> BatchQuery<T>(string pattern)
{
var keys = QueryKeys(pattern);
if (!keys.Any())
{
return new List<T>();
}
var redisValues = db.SetCombine(SetOperation.Union, keys.ToArray());
return redisValues
?.Select(g => JsonConvert.DeserializeObject<T>(g.ToString()))
?.ToList();
}
/// <summary>
/// 模糊查询
/// </summary>
/// <param name="pattern"></param>
public IEnumerable<RedisKey> QueryKeys(string pattern)
{
return redisServer.Keys(db.Database, GenerateKey(pattern));
}
#endregion
/// <summary>
/// 生成带固定前缀Otc的Key
/// </summary>
/// <param name="originKey"></param>
/// <returns></returns>
public string GenerateKey(string originKey)
{
return $"Otc:{originKey}";
}
/// <summary>
/// 保存不带前缀的key
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool StringSetWithNoPrefix<T>(string key, object value,TimeSpan? timeSpan) where T : class
{
return db.StringSet(key, JsonConvert.SerializeObject(value), timeSpan);
}
}
}