261 lines
8.8 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|