.Net Core Redis缓存接口以及实现
群里的老表说用 StackExchange.Redis 遇到超时问题,(类似下面这种报错)
Timeout performing GET my_141 (5000ms), inst: 30, qu: 0, qs: 20, in: 20320, serverEndpoint: xxxxxx: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
首先去掉同步方法 朕的大清都亡了,还用同步呢
首先去掉同步方法 朕的大清都亡了,还用同步呢
首先去掉同步方法 朕的大清都亡了,还用同步呢
网络请求之类的尽量要用异步
因此给出我用的方法以供参考:
我用的类库版本:
<PackageReference Include="StackExchange.Redis" Version="2.2.88" />
redis版本:
6.2.6
我这里一共用了三个文件:
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 IConfiguration _configuration;
private readonly ILogger<RedisManage> _logger;
public RedisManage(ILogger<RedisManage> logger,
IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
ConfigurationOptions options = ReadRedisSetting();
if (options == null)
{
_logger.LogError("Redis数据库配置有误");
}
this._configOptions = options;
this._redisConnection = ConnectionRedis();
}
private ConfigurationOptions ReadRedisSetting()
{
try
{
var config = _configuration.GetSection("Redis").Get<RedisConfig>();
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}");
throw;
}
}
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}");
throw;
}
}
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