Как ConnectionMultiplexer имеет дело с разъединениями?

Документация Basic Usage для StackExchange.Redis объясняет, что ConnectionMultiplexer является долговечным и, как ожидается, будет использоваться повторно.

Но как насчет нарушения соединения с сервером? ConnectionMultiplexer автоматически восстанавливается, или необходимо написать код, как в этот ответ (цитируя этот ответ):

        if (RedisConnection == null || !RedisConnection.IsConnected)
        {
            RedisConnection = ConnectionMultiplexer.Connect(...);
        }
        RedisCacheDb = RedisConnection.GetDatabase();

Является ли приведенным выше кодом что-то хорошее для обработки восстановления из разъединений, или это может привести к нескольким экземплярам ConnectionMultiplexer? Вдоль тех же строк, как должно интерпретироваться свойство IsConnected?

[Помимо этого, я считаю, что приведенный выше код - довольно плохая форма ленивой инициализации, особенно в многопоточных средах - см. статья Джона Скита о синглтонах].

Ответ 1

Вот шаблон рекомендованный командой Azure Redis Cache:

private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => {
    return ConnectionMultiplexer.Connect("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
});

public static ConnectionMultiplexer Connection {
    get {
        return lazyConnection.Value;
    }
}

Несколько важных моментов:

  • Он использует Lazy <T> для обработки инициализации без потоков
  • Он устанавливает "abortConnect = false", что означает, что если попытка первоначального подключения не удалась, ConnectionMultiplexer будет тихо повторять в фоновом режиме, а не исключать исключение.
  • Это свойство не проверяет свойство IsConnected, поскольку ConnectionMultiplexer будет автоматически повторять попытку в фоновом режиме, если соединение будет удалено.

Ответ 2

Да, вам нужен такой тип проверки, чтобы исправить неработающие подключения. Также следует учитывать и некоторую безопасность потоков. Вот как я обычно это делаю:

private static ConnectionMultiplexer _redis;
private static readonly Object _multiplexerLock = new Object();

private void ConnectRedis()
{
    try
    {
        _redis = ConnectionMultiplexer.Connect("...<connection string here>...");
    }
    catch (Exception ex)
    {
        //exception handling goes here
    }
}


private ConnectionMultiplexer RedisMultiplexer
{
    get
    {
        lock (_multiplexerLock)
        {
            if (_redis == null || !_redis.IsConnected)
            {
                ConnectRedis();
            }
            return _redis;
        }
    }
}

Затем я использую свойство RedisMultiplexer везде, где мне нужно вызывать конечную точку Redis. Обычно я не сохраняю результат вызова GetDatabase(), потому что документация говорит о довольно легком вызове.