编辑 2015-05-02:虽然 StackExchange.Redis 客户端的更高版本明确应该在内部和自动处理这种“丢失的连接”->“重新连接”逻辑,但我的测试已经毫无疑问地表明他们没有不能成功地完成它,因此仍然需要这种东西,至少在繁忙的环境中是这样。我将下面的代码从我的缓存层中抽出一段时间,结果出现了数以万计的连接失败错误。我把它放回去,那些都消失了。
编辑 2015-02-24:不再需要此方法。最新版本的StackExchange.Redis
客户端正确处理断开连接 - 他们会自动重新连接,下面的解决方法只会干扰事情。出于历史目的将其保留在这里,但我的建议是忽略它。
以下是我的包装器中的一些方法,SimpleCacheRedis<T>
它们显示了我如何处理问题:
public async Task<TValue> GetAsync(string key, Func<Task<TValue>> missingFunc)
{
key = GetKey(key);
var value = default(TValue);
try
{
var db = _connection.GetDatabase();
var str = await db.StringGetAsync(key);
if (!str.IsNullOrEmpty)
{
value = _jsonSerializer.Deserialize<TValue>(str);
}
}
catch (RedisConnectionException ex)
{
HandleRedisConnectionError(ex);
}
catch (Exception ex)
{
_logger.Error("Error retrieving item '" + key +
"' from Redis cache; falling back to missingFunc(). Error = " + ex);
}
if (value == default(TValue))
{
present = false;
value = await missingFunc();
await PerformAddAsync(key, value);
}
return value;
}
private void HandleRedisConnectionError(RedisConnectionException ex)
{
_logger.Error("Connection error with Redis cache; recreating connection for the next try, and falling back to missingFunc() for this one. Error = " + ex.Message);
Task.Run(async () =>
{
try
{
await CreateConnectionAsync();
}
catch (Exception genEx)
{
_logger.Error("Unable to recreate redis connection (sigh); bailing for now: " + genEx.Message);
}
});
}
private async Task CreateConnectionAsync()
{
if (_attemptingToConnect) return;
var sw = new StringWriter();
try
{
_attemptingToConnect = true;
_connection = await ConnectionMultiplexer.ConnectAsync(_redisCs, sw);
}
catch (Exception ex)
{
_logger.Error("Unable to connect to redis async: " + ex);
_logger.Debug("internal log: \r\n" + sw);
throw;
}
finally
{
_attemptingToConnect = false;
}
}
基本上,如果我发现由于 a 无法连接到 Redis RedisConnectionException
,我会分拆一个单独的async
任务来重新创建共享连接。当然,那个调用可能会失败,但无论如何,在那段时间调用都会失败。一旦成功,任何新调用都将使用该新(重新)创建的连接。就像我上面说的,有点无聊。
我的情况可能与您的情况有些不同,因为我没有将 Redis 用作永久存储,而只是用作缓存。这意味着丢失 redis 连接的唯一影响是我需要从数据库而不是缓存中检索结果。所以我可以稍微松散地对待某些事情。