1

我已经实现了以下版本的 ServiceStack .net Core Redis 库:

服务堆栈.Redis.Core 5.9.2

我正在使用该库访问我创建的 Redis 缓存,以使用 .NET Core 3.1 为我的 AWS 无服务器应用程序保留值。我已经为 ServiceStack Redis 支付了商业许可。

在尝试创建 Redis 客户端时,我的应用程序会定期且无警告地捕获以下错误:

Exception: System.Exception: No master found in: redis-cluster-api-prd-lcs.in-xxxxxxxx:6379
   at ServiceStack.Redis.RedisResolver.CreateRedisClient(RedisEndpoint config, Boolean master) in C:\BuildAgent\work\b2a0bfe2b1c9a118\src\ServiceStack.Redis\RedisResolver.cs:line 116
   at ServiceStack.Redis.RedisResolver.CreateMasterClient(Int32 desiredIndex) in C:\BuildAgent\work\b2a0bfe2b1c9a118\src\ServiceStack.Redis\RedisResolver.cs:line 142
   at ServiceStack.Redis.RedisManagerPool.GetClient() in C:\BuildAgent\work\b2a0bfe2b1c9a118\src\ServiceStack.Redis\RedisManagerPool.cs:line 174
   at LCSApi.UtilityCommand.Cache.IsCacheValueExists(String cacheKey, RedisManagerPool pool) in D:\a\1\s\testapi\Utility.cs:line 167
   at LCSApi.Functions.LcsConfigurationSweeper(ILambdaContext context) in D:\a\1\s\testapi\Function.cs:line 2028
Exception: System.Exception: No master found in: redis-cluster-api-prd-lcs.in

其他时候,相同的代码可以正常工作。我的实现很简单:

private readonly RedisManagerPool _redisClient;

 _redisClient = new RedisManagerPool(Environment.GetEnvironmentVariable("CACHE_URL") + ":" +
Environment.GetEnvironmentVariable("CACHE_PORT"));

        public static T GetCacheValue<T>(string cacheKey, RedisManagerPool pool)
        {
            T cacheValue;

            try
            {
                //StackExchange.Redis.IDatabase cache = Functions._redisConnect.GetDatabase();
                //string value = cache.StringGet(cacheKey);
                //cacheValue = (T)Convert.ChangeType(value, typeof(T));
                using (var client = pool.GetClient())
                {
                    client.RetryCount = Convert.ToInt32(Environment.GetEnvironmentVariable("CACHE_RETRY_COUNT"));
                    client.RetryTimeout = Convert.ToInt32(Environment.GetEnvironmentVariable("CACHE_RETRY_TIMEOUT"));
                    cacheValue = client.Get<T>(cacheKey);
                }
            }
            catch (Exception ex)
            {
                //Console.WriteLine($"[CACHE_EXCEPTION] {ex.ToString()}");
                cacheValue = GetParameterSSMFallback<T>(cacheKey);
                //Console.WriteLine($"[CACHE_EXCEPTION] Fallback SSM parameter --> {cacheValue}");
            }

            return cacheValue;
        }

碰巧我不得不编写一个“回退”例程来从其来源的 AWS Parameter Store 中获取值。不理想。这是 Redis 的配置:

在此处输入图像描述

我可以在任何地方在线找到关于此错误的任何信息。我尝试注册 ServiceStack 论坛但没有成功,即使我有商业许可证,由于某种原因它不会让我注册。有人可以帮忙吗?

4

1 回答 1

0

该错误是由于网络实例未连接到 redis ROLE命令所标识的主实例。这可能发生在 ElastiCache 故障转移期间,最终主实例将以原始 DNS 名称返回:

Amazon ElastiCache for Redis 将通过获取新的服务资源来修复节点,然后将节点的现有 DNS 名称重定向到新的服务资源。因此,Redis 节点的 DNS 名称保持不变,但 Redis 节点的 IP 地址会随时间变化。

ServiceStack.Redis将尝试使用所有指定的主连接(通常只有 1 个)连接到主实例。如果它无法连接到主实例,它必须放弃,因为客户端希望在读/写主实例上执行操作。

如果预计主实例将以相同的 DNS 名称返回,我们可以使用自定义IRedisResolver在指定的时间段内不断重试主实例的同一连接,例如:

public class ElasticCacheRedisResolver : RedisResolver 
{
    public override RedisClient CreateRedisClient(RedisEndpoint config, bool master)
    {
        if (master)
        {
            //ElastiCache Redis will failover & retain same DNS for master
            var firstAttempt = DateTime.UtcNow;
            Exception firstEx = null;
            var retryTimeSpan = TimeSpan.FromMilliseconds(config.RetryTimeout);
            var i = 0;
            while (DateTime.UtcNow - firstAttempt < retryTimeSpan) {
                try
                {
                    var client = base.CreateRedisClient(config, master:true);
                    return client;
                }
                catch (Exception ex)
                {
                    firstEx ??= ex;
                    ExecUtils.SleepBackOffMultiplier(++i);
                }
            }
            throw new TimeoutException(
              $"Could not resolve master within {config.RetryTimeout}ms RetryTimeout", firstEx);
        }
        return base.CreateRedisClient(config, master:false);
    }
}

您使用 Redis 客户端管理器进行配置,例如:

private static readonly RedisManagerPool redisManager;

redisManager = new RedisManagerPool(...) {
    RedisResolver = new ElasticCacheRedisResolver()
};

注意:您应该只使用 1 个Redis 客户端管理器的共享实例,这样RedisManagerPool共享同一个连接池。如果包含 的类redisManager不是单例,则应将其分配给静态字段,以确保使用相同的单例实例来检索客户端。

于 2020-09-24T13:11:24.717 回答