【问题标题】:Handling Azure Redis Cache exceptions处理 Azure Redis 缓存异常
【发布时间】:2019-10-02 06:10:30
【问题描述】:

我正在使用 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();
    }

我在下面的另一个课程中使用这个 -

    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 时是否还有其他需要处理的异常?

【问题讨论】:

    标签: redis stackexchange.redis azure-redis-cache


    【解决方案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 返回的对象是cheap pass-thru object,不需要存储。

    • GetFromCacheAsync / SaveInCacheAsync - 方法可以向外部抛出异常并且没关系。您可以申请Retry-policy 来解决暂时性故障。

    • 如果您有任何 IoC 容器,那么它应该创建具有单个实例范围的 RedisCacheManager(例如,Autofac registration

    【讨论】:

    • 感谢您的回答。您看到在初始化期间存储从 GetDatabase 返回的对象有什么缺点吗?如果我们使用管理器的单个实例,即使我们存储了数据库值,它也会被重用,对吗?
    猜你喜欢
    • 2019-01-28
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 2016-09-10
    • 2019-05-17
    相关资源
    最近更新 更多