从Redis连接丢失中恢复

Kon*_*kov 6 c# redis stackexchange.redis

在Redis连接丢失后,我正在寻找恢复[在多线程环境中]的参考实现.到目前为止无法找到任何有意义的东西.

安装程序:我有一个Azure辅助角色,它在多个线程中运行相同的代码(ThreadProc).最初,我有每个Redis操作之前的静态ConnectionMultiplexer和.GetDatabase().这根本没有通过压力测试(一旦负载从低到中增加,就会出现大量的"无法连接"错误).我把它改成了这个:

static readonly ConnectionMultiplexer _connection = ConnectionMultiplexer.Connect(...);
static readonly IDatabase _cache = _connection.GetDatabase();

void ThreadProc() // running in multiple threads
{
    while (true)
    {
      // using _cache here
    }
}
Run Code Online (Sandbox Code Playgroud)

即使在高负载(每个工作者角色实例1000个操作/秒)之前,这仍然很好,直到我得到"没有连接可用于服务此操作",并且事情无法恢复.

请告诉我可以从间歇性连接问题中恢复的正确/推荐代码.

Ken*_*ith 10

编辑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;
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,如果我发现由于a而无法连接到Redis RedisConnectionException,我会分离一个单独的async任务来重新创建共享连接.当然,这个电话可能会失败,但无论如何,电话会在那段时间内失败.一旦成功,任何新的调用都将使用新的(重新)创建的连接.就像我上面说的那样,有点无聊.

我的情况可能与你的情况有点不同,因为我不是将Redis用作永久存储,而是将其作为缓存.这意味着丢失的redis连接的唯一影响是我需要从DB而不是从缓存中检索结果.所以我可以稍稍松懈地对待某些事情.