Обработка исключений кэша Redis для Azure

Я использую кэш Redis для Azure для разработки и хочу проверить, как я обрабатываю исключения. Согласно лучшим практикам, можно столкнуться с 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();
    }

И я потребляю это ниже в другом классе -

    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);
        }
        }

Я не уверен, что id, вызывающий конструктор в блоке catch, является хорошей практикой. И есть ли другие исключения, которые мне нужно будет обработать при вызове StringGetAsync и StringSetAsync?


person Kruti Joshi    schedule 15.05.2019    source источник


Ответы (1)


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 для Redis не установлено), а из конструктора не должно генерироваться никаких исключений Redis

  • Объект, возвращаемый из GetDatabase, представляет собой дешевый сквозной объект, и хранить его не нужно.

  • Методы GetFromCacheAsync / SaveInCacheAsync могут вызывать исключение извне, и это нормально. Вы можете применить политику повторных попыток для устранения временных ошибок.

  • Если у вас есть IoC-контейнер, он должен создать RedisCacheManager с одной областью действия экземпляра (например, Регистрация Autofac)

person vladimir    schedule 17.05.2019
comment
Спасибо за Ваш ответ. Видите ли вы какой-либо недостаток в хранении объекта, возвращенного из GetDatabase во время инициализации? Если мы используем единственный экземпляр менеджера, даже если мы сохраним значение базы данных, оно будет использовано повторно, верно? - person Kruti Joshi; 20.08.2019