处理 Azure Redis 缓存异常
Handling Azure Redis Cache exceptions
我正在使用 Azure Redis 缓存进行开发,想验证我处理异常的方式。根据最佳实践,可能会遇到 RedisConnectionExceptions 并解决此问题,我们必须处理旧的 ConnectionMultiplexer 并创建一个新的。如果将 abortConnect 设置为 false,则多路复用器将在不抛出错误的情况下静默重试连接。因此,如果抛出异常,它只会在尝试重新连接但仍然失败后才发生。我对此的理解正确吗?
这是我的连接字符串 -
cachename.redis.cache.windows.net:6380,password=Password,ssl=True,abortConnect=False
我相信连接异常只会在您尝试在多路复用器上调用 GetConnection() 时发生。在下面找到我的代码 -
static Lazy<ConnectionMultiplexer> multiplexer = CreateMultiplexer();
public static ConnectionMultiplexer GetConnection() => multiplexer.Value;
private static Lazy<ConnectionMultiplexer> CreateMultiplexer()
{
return new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(connectionString));
}
private static void CloseMultiplexer(Lazy<ConnectionMultiplexer> oldMultiplexer)
{
if (oldMultiplexer != null)
{
oldMultiplexer.Value.Close();
}
}
public static void Reconnect()
{
var oldMultiplexer = multiplexer;
CloseMultiplexer(multiplexer);
multiplexer = CreateMultiplexer();
}
我正在另一个 class -
中使用它
public class RedisCacheManager
{
private static IDatabase _cache;
private TimeSpan expiry = new TimeSpan(hours: 6, minutes: 0, seconds: 0);
public RedisCacheManager()
{
try
{
_cache = RedisCacheHelper.GetConnection().GetDatabase();
}
catch(RedisConnectionException)
{
RedisCacheHelper.Reconnect();
new RedisCacheManager();
}
}
public async Task<RedisValue[]> GetFromCacheAsync(List<string> keys)
{
var cacheValues = await _cache.StringGetAsync(keys.Select(k => (RedisKey)k).ToArray());
return cacheValues;
}
public async Task SaveInCacheAsync<TValue>(Dictionary<string, TValue> kvps)
{
var tasks = new List<Task>();
foreach(var kvp in kvps)
{
tasks.Add(_cache.StringSetAsync(kvp.Key, JsonConvert.SerializeObject(kvp), expiry));
}
await Task.WhenAll(tasks);
}
}
我不确定在 catch 块中调用构造函数是否是一个好习惯。在调用 StringGetAsync 和 StringSetAsync 时是否还有其他需要处理的异常?
CacheManager 可以如下所示:
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public sealed class RedisCacheManager : IDisposable
{
private readonly TimeSpan _expiry;
private readonly Lazy<ConnectionMultiplexer> _lazyConnection;
private ConnectionMultiplexer Connection { get => _lazyConnection.Value; }
public RedisCacheManager(string connectionString, TimeSpan expiry)
{
_expiry = expiry;
_lazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(connectionString));
}
public async Task<RedisValue[]> GetFromCacheAsync(IEnumerable<string> keys)
{
var cacheValues = await Connection.GetDatabase()
.StringGetAsync(keys.Select(key => (RedisKey)key).ToArray()).ConfigureAwait(false);
return cacheValues;
}
public async Task SaveInCacheAsync<TValue>(Dictionary<string, TValue> kvps)
{
var tasks = kvps
.Select(kvp => Connection.GetDatabase().StringSetAsync(kvp.Key, JsonConvert.SerializeObject(kvp), _expiry))
.ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false);
}
public void Dispose()
{
if (_lazyConnection.IsValueCreated)
{
_lazyConnection.Value.Dispose();
}
}
}
使用:
public readonly static RedisCacheManager RedisCacheManager = new RedisCacheManager("connection string", TimeSpan.FromHours(6));
备注:
它的目的是 abortConnect=false(这意味着即使未建立与 Azure Cache for Redis 的连接,调用也会成功)并且来自构造函数不应抛出任何 Redis-exceptions
GetDatabase返回的对象是cheap pass-thru object,不需要存储
GetFromCacheAsync / SaveInCacheAsync - 方法可以向外部抛出异常并且可以。您可以应用 Retry-policy 来解决瞬态故障。
如果你有 IoC-container 那么它应该创建具有单个实例范围的 RedisCacheManager(例如,Autofac registration)
我正在使用 Azure Redis 缓存进行开发,想验证我处理异常的方式。根据最佳实践,可能会遇到 RedisConnectionExceptions 并解决此问题,我们必须处理旧的 ConnectionMultiplexer 并创建一个新的。如果将 abortConnect 设置为 false,则多路复用器将在不抛出错误的情况下静默重试连接。因此,如果抛出异常,它只会在尝试重新连接但仍然失败后才发生。我对此的理解正确吗? 这是我的连接字符串 -
cachename.redis.cache.windows.net:6380,password=Password,ssl=True,abortConnect=False
我相信连接异常只会在您尝试在多路复用器上调用 GetConnection() 时发生。在下面找到我的代码 -
static Lazy<ConnectionMultiplexer> multiplexer = CreateMultiplexer();
public static ConnectionMultiplexer GetConnection() => multiplexer.Value;
private static Lazy<ConnectionMultiplexer> CreateMultiplexer()
{
return new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(connectionString));
}
private static void CloseMultiplexer(Lazy<ConnectionMultiplexer> oldMultiplexer)
{
if (oldMultiplexer != null)
{
oldMultiplexer.Value.Close();
}
}
public static void Reconnect()
{
var oldMultiplexer = multiplexer;
CloseMultiplexer(multiplexer);
multiplexer = CreateMultiplexer();
}
我正在另一个 class -
中使用它 public class RedisCacheManager
{
private static IDatabase _cache;
private TimeSpan expiry = new TimeSpan(hours: 6, minutes: 0, seconds: 0);
public RedisCacheManager()
{
try
{
_cache = RedisCacheHelper.GetConnection().GetDatabase();
}
catch(RedisConnectionException)
{
RedisCacheHelper.Reconnect();
new RedisCacheManager();
}
}
public async Task<RedisValue[]> GetFromCacheAsync(List<string> keys)
{
var cacheValues = await _cache.StringGetAsync(keys.Select(k => (RedisKey)k).ToArray());
return cacheValues;
}
public async Task SaveInCacheAsync<TValue>(Dictionary<string, TValue> kvps)
{
var tasks = new List<Task>();
foreach(var kvp in kvps)
{
tasks.Add(_cache.StringSetAsync(kvp.Key, JsonConvert.SerializeObject(kvp), expiry));
}
await Task.WhenAll(tasks);
}
}
我不确定在 catch 块中调用构造函数是否是一个好习惯。在调用 StringGetAsync 和 StringSetAsync 时是否还有其他需要处理的异常?
CacheManager 可以如下所示:
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public sealed class RedisCacheManager : IDisposable
{
private readonly TimeSpan _expiry;
private readonly Lazy<ConnectionMultiplexer> _lazyConnection;
private ConnectionMultiplexer Connection { get => _lazyConnection.Value; }
public RedisCacheManager(string connectionString, TimeSpan expiry)
{
_expiry = expiry;
_lazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(connectionString));
}
public async Task<RedisValue[]> GetFromCacheAsync(IEnumerable<string> keys)
{
var cacheValues = await Connection.GetDatabase()
.StringGetAsync(keys.Select(key => (RedisKey)key).ToArray()).ConfigureAwait(false);
return cacheValues;
}
public async Task SaveInCacheAsync<TValue>(Dictionary<string, TValue> kvps)
{
var tasks = kvps
.Select(kvp => Connection.GetDatabase().StringSetAsync(kvp.Key, JsonConvert.SerializeObject(kvp), _expiry))
.ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false);
}
public void Dispose()
{
if (_lazyConnection.IsValueCreated)
{
_lazyConnection.Value.Dispose();
}
}
}
使用:
public readonly static RedisCacheManager RedisCacheManager = new RedisCacheManager("connection string", TimeSpan.FromHours(6));
备注:
它的目的是 abortConnect=false(这意味着即使未建立与 Azure Cache for Redis 的连接,调用也会成功)并且来自构造函数不应抛出任何 Redis-exceptions
GetDatabase返回的对象是cheap pass-thru object,不需要存储
GetFromCacheAsync / SaveInCacheAsync - 方法可以向外部抛出异常并且可以。您可以应用 Retry-policy 来解决瞬态故障。
如果你有 IoC-container 那么它应该创建具有单个实例范围的 RedisCacheManager(例如,Autofac registration)