.Net Core Redis缓存接口以及实现

发布时间 2023-11-13 08:45:00作者: 邓振振

群里的老表说用 StackExchange.Redis 遇到超时问题

Timeout performing GET my_141 (5000ms), inst: 30, qu: 0, qs: 20, in: 20320, serverEndpoint: 172.16.3.119:6379, mgr: 10 of 10 available, clientName: s-119, IOCP: (Busy=0,Free=1000,Min=1,Max=1000), WORKER: (Busy=120,Free=32747,Min=1,Max=32767), v: 2.0.571.20511(Please take a look at this article for some common client-side issues that can cause timeouts: https://stackexchange.github.io/StackExchange.Redis/Timeouts))

这可能是用这个类库的通病,不过官方也给出了可能性
https://stackexchange.github.io/StackExchange.Redis/Timeouts
因此我分享下自己使用的ICache和对应的实现

首先去掉同步方法
首先去掉同步方法
首先去掉同步方法
都2025年了还用同步

我用的类库版本:

<PackageReference Include="StackExchange.Redis" Version="2.2.88" />

redis版本:

6.2.6

我这里一共用了三个文件:
image

ICache.cs
定义了一些常用的Get,Set,Hash, ZIncr等操作

  public interface ICache
  {
      Task<T> GetAsync<T>(string key);

      Task<List<T>> GetPatternAsync<T>(string patternKey);

      Task<bool> SetAsync<T>(string key, T item, int timeoutSeconds);

      Task<string[]> KeysAsync(string patternKey, int database = 0);

      Task<long> RemoveAsync(params string[] keys);

      Task<long> RemovePatternAsync(string patternKey);

      Task<T> HashGetAsync<T>(string key, string field);

      Task<IEnumerable<T>> HashGetAsync<T>(string key);

      Task<bool> HashSetAsync<T>(string key, string field, T item);

      Task<string[]> HashKeysAsync(string key);

      Task<long> HashRemoveAsync(string key, params string[] fields);

      Task<long> HashLengthAsync(string key);

      Task<List<(string field, string value)>> HashScanAsync(string key, string pattern, int count);

      Task<decimal> ZIncrByAsync(string key, string member, decimal increment = 1);

      Task<decimal> ZScoreAsync(string key, string member);

      Task<bool> ZScoreSetAsync(string key, string member, decimal score);
  }

RedisManage.cs
实现了缓存接口

 public class RedisManage : ICache
 {
     public volatile ConnectionMultiplexer _redisConnection;
     private readonly object _redisConnectionLock = new object();
     private readonly ConfigurationOptions _configOptions;
     private readonly ILogger<RedisManage> _logger;

     public RedisManage(ILogger<RedisManage> logger)
     {
         _logger = logger;
         ConfigurationOptions options = ReadRedisSetting();
         if (options == null)
         {
             _logger.LogError("Redis数据库配置有误");
         }

         this._configOptions = options;
         this._redisConnection = ConnectionRedis();
     }

     private ConfigurationOptions ReadRedisSetting()
     {
         try
         {
             RedisConfig config = ConfigHelper.Get<RedisConfig>("Redis");
             ConfigurationOptions options = new ConfigurationOptions
             {
                 EndPoints =
                         {
                             {
                                 config.Ip,
                                 config.Port
                             }
                         },
                 ClientName = $"HANDLOONG_{Guid.NewGuid()}",
                 Password = config.Password,
                 ConnectTimeout = config.Timeout,
                 DefaultDatabase = config.DefaultDataBase,//不配置默认是0
             };
             return options;
         }
         catch (Exception ex)
         {
             _logger.LogError($"获取Redis配置信息失败:{ex.Message}");
             return null;
         }
     }

     private ConnectionMultiplexer ConnectionRedis()
     {
         if (this._redisConnection != null && this._redisConnection.IsConnected)
         {
             return this._redisConnection; // 已有连接,直接使用
         }
         lock (_redisConnectionLock)
         {
             if (this._redisConnection != null)
             {
                 this._redisConnection.Dispose(); // 释放,重连
             }
             try
             {
                 this._redisConnection = ConnectionMultiplexer.Connect(_configOptions);
             }
             catch (Exception ex)
             {
                 _logger.LogError($"Redis服务启动失败:{ex.Message}");
             }
         }
         return this._redisConnection;
     }

     public async Task<T> GetAsync<T>(string key)
     {
         var ret = await _redisConnection.GetDatabase().StringGetAsync(key);
         if (!string.IsNullOrEmpty(ret.ToString()))
             return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(ret.ToString());
         return default;
     }

     public async Task<List<T>> GetPatternAsync<T>(string patternKey)
     {
         var res = new List<T>();
         var keys = await KeysAsync(patternKey);
         if (keys != null && keys.Length > 0)
         {
             foreach (var key in keys)
             {
                 var data = await GetAsync<T>(key);
                 res.Add(data);
             }
         }
         return res;
     }

     public async Task<T> HashGetAsync<T>(string key, string field)
     {
         var ret = await _redisConnection.GetDatabase().HashGetAsync(key, field);
         if (ret.ToString() != null)
             return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(ret.ToString());
         return default;
     }

     public async Task<IEnumerable<T>> HashGetAsync<T>(string key)
     {
         var keys = await HashKeysAsync(key);
         var fields = keys.Select(x => (RedisValue)x);
         var result = (await _redisConnection.GetDatabase().HashGetAsync(key, fields.ToArray())).Select(o => o.ToString());
         return result.Select(o => Newtonsoft.Json.JsonConvert.DeserializeObject<T>(o));
     }

     public async Task<string[]> HashKeysAsync(string key)
     {
         var ret = (await _redisConnection.GetDatabase().HashKeysAsync(key))
             .Select(o => o.ToString());
         return ret.ToArray();
     }

     public async Task<long> HashLengthAsync(string key)
     {
         return await _redisConnection.GetDatabase().HashLengthAsync(key);
     }

     public async Task<long> HashRemoveAsync(string key, params string[] fields)
     {
         if (fields == null || fields.Length == 0)
             fields = await HashKeysAsync(key);

         var fieldsRv = fields?.Select(x => (RedisValue)x);
         if (fieldsRv != null)
             return await _redisConnection.GetDatabase().HashDeleteAsync(key, fieldsRv.ToArray());

         return 0;
     }

     public async Task<List<(string field, string value)>> HashScanAsync(string key, string pattern, int count)
     {
         var res = new List<(string field, string value)>();

         var scanRes = _redisConnection.GetDatabase().HashScanAsync(key, pattern, count, 0);
         await foreach (var item in scanRes)
         {
             res.Add((item.Name, item.Value));
         }
         return res;
     }

     public async Task<bool> HashSetAsync<T>(string key, string field, T item)
     {
         return await _redisConnection.GetDatabase().HashSetAsync(key, field, Newtonsoft.Json.JsonConvert.SerializeObject(item));
     }

     public async Task<string[]> KeysAsync(string patternKey, int database = 0)
     {
         var result = new List<string>();
         var points = _redisConnection.GetEndPoints();
         if (points?.Length > 0)
         {
             foreach (var point in points)
             {
                 var server = _redisConnection.GetServer(point);
                 var keys = server.KeysAsync(database: database, pattern: patternKey);
                 await foreach (var key in keys)
                 {
                     result.Add(key);
                 }
             }
         }
         return result.ToArray();
     }

     public async Task<long> RemoveAsync(params string[] keys)
     {
         var keysRV = keys.Select(x => (RedisKey)x);
         return await _redisConnection.GetDatabase().KeyDeleteAsync(keysRV.ToArray());
     }

     public async Task<long> RemovePatternAsync(string patternKey)
     {
         var keys = await KeysAsync(patternKey);
         return await RemoveAsync(keys);
     }

     public async Task<bool> SetAsync<T>(string key, T item, int timeoutSeconds)
     {
         return await _redisConnection.GetDatabase().StringSetAsync(key, Newtonsoft.Json.JsonConvert.SerializeObject(item), TimeSpan.FromSeconds(timeoutSeconds));
     }

     public async Task<decimal> ZIncrByAsync(string key, string member, decimal increment = 1)
     {
         return (decimal)(await _redisConnection.GetDatabase().SortedSetIncrementAsync(key, member, (double)increment));
     }

     public async Task<decimal> ZScoreAsync(string key, string member)
     {
         return (decimal)(await _redisConnection.GetDatabase().SortedSetScoreAsync(key, member) ?? 0);
     }

     public async Task<bool> ZScoreSetAsync(string key, string member, decimal score)
     {
         return await _redisConnection.GetDatabase().SortedSetAddAsync(key, member, (double)score);
     }
 }

RedisConfig配置类

  public class RedisConfig
  {
      public string Ip { get; set; }
      public int Port { get; set; }
      public string Name { get; set; }
      public string Password { get; set; }
      public int Timeout { get; set; } = 5000;
      public int DefaultDataBase { get; set; }
  }

appsetting.json

  "Redis": {
    "Ip": "127.0.0.1",
    "Port": 6379,
    "Password": "HandLoongDev",
    "Timeout": 5000
  }

startup注入:

 services
     .AddSingleton<ICache, RedisManage>()

使用:

/// <summary>
/// 字段
/// </summary>
private readonly ICache _cache;

/// <summary>
/// 构造函数
/// </summary>
/// <param name="cache"></param>
public TestRedisController(ICache cache)
{
    _cache = cache;
}

参考链接:
https://www.cnblogs.com/taotaozhuanyong/p/13794499.html