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; private readonly ConnectionMultiplexer _redisConnector; private readonly IServer redisServer; private IDatabase db; public YLRedisCache(IOptions 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("")); } } /// /// 缓存是否可用 /// /// 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(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(string key, object value) { return StringSet(key, JsonConvert.SerializeObject(value)); } public T StringGet(string key) where T : class { var value = StringGet(key); if (string.IsNullOrEmpty(value)) { return null; } return JsonConvert.DeserializeObject(value); } public T StringGetWithNoPrefix(string key) where T : class { var value = db.StringGet(key); if (string.IsNullOrEmpty(value)) { return null; } return JsonConvert.DeserializeObject(value); } #region Batch Operate private async Task> SerializeObject(List objs) { return await Task.Factory.StartNew(() => { List redisValues = new List(); foreach (T obj in objs) { redisValues.Add(JsonConvert.SerializeObject(obj)); } return redisValues; }); } private async Task SerializeObject(T obj) { return await Task.Factory.StartNew(() => { return JsonConvert.SerializeObject(obj); }); } /// /// 批量增加 /// /// /// /// /// public async Task BatchAdd(string preKey,List values, int setSize = 1000) { //Stopwatch stopwatch1= Stopwatch.StartNew(); //List tasktest=new List(); //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()); // } // dicRedisValue[page].Add(JsonConvert.SerializeObject(values[i])); //} //var batch = db.CreateBatch(); //List tasks = new List(); //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>> dicRedisValue = new Dictionary>>(); 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 tasks = new List(); // 每组对应一个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}"); } /// /// 批量删除 /// /// /// 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; } /// /// 批量查询 /// /// /// /// public List BatchQuery(string pattern) { var keys = QueryKeys(pattern); if (!keys.Any()) { return new List(); } var redisValues = db.SetCombine(SetOperation.Union, keys.ToArray()); return redisValues ?.Select(g => JsonConvert.DeserializeObject(g.ToString())) ?.ToList(); } /// /// 模糊查询 /// /// public IEnumerable QueryKeys(string pattern) { return redisServer.Keys(db.Database, GenerateKey(pattern)); } #endregion /// /// 生成带固定前缀Otc的Key /// /// /// public string GenerateKey(string originKey) { return $"Otc:{originKey}"; } /// /// 保存不带前缀的key /// /// /// /// /// public bool StringSetWithNoPrefix(string key, object value,TimeSpan? timeSpan) where T : class { return db.StringSet(key, JsonConvert.SerializeObject(value), timeSpan); } } }