首页 > 解决方案 > ServiceStack.Redis 对整数响应和零长度响应的未知回复

问题描述

我在生产中使用 ServiceStack 的 Redis 客户端时遇到了错误。

错误是 RedisClient.ContainsKey 的“整数响应上的未知回复:43OK”和RedisClient.Get“零长度响应” 。

该应用程序使用 Redis Sentinel 和 PoolRedisClientManager。我有包装类来初始化 Redis 实例以及 Redis 操作:

      public class RedisFactory : IRedisFactory
      {
        private RedisSentinel redisSentinel;
        private IRedisClientsManager clientsManager;

        public void Init()
        {
        RedisConfig.DefaultRetryTimeout = RETRY_TIMEOUT; //5000ms
        RedisClientManagerConfig config = new RedisClientManagerConfig()
        {
            MaxReadPoolSize = MAX_POOL_SIZE, //40
            MaxWritePoolSize = MAX_POOL_SIZE, //40
            DefaultDb = DEFAUTL_DB
        };

        redisSentinel = new RedisSentinel(sentinelHosts, masterName: masterName)
        {
            RedisManagerFactory = (master, slaves) => new PooledRedisClientManager(master.ToList(), slaves.ToList(), config)
            {
                NamespacePrefix = NAMESPACE_PREFIX
            },
            OnFailover = manager => _log.Info($"Fail over at {DateTime.Now}"),
            OnWorkerError = ex => _log.Error($"Sentinel worker error \n {ex}"),
            OnSentinelMessageReceived = (channel, msg) => _log.Info($"Received '{channel}' on channel '{msg}' from Sentinel")

        };

        clientsManager = redisSentinel.Start();
    }

    public bool KeyExisted(string key)
    {
        Func<string, bool> keyExisted = delegate (string k)
        {
            using (var slave = clientsManager.GetReadonlyClient())
            {
                var existed = slave.ContainsKey(k);
                return existed;
            }
        };

        return Execute(nameof(keyExisted), key, keyExisted);
    }

    public T Get<T>(string key)
    {
        Func<string, T> get = delegate (string k)
        {
            using (var slave = clientsManager.GetReadonlyClient())
            {
                var value = slave.Get<T>(k);
                return value;
            }
        };

        return Execute(nameof(get), key, get);
    }

    private T Execute<T>(string operation, string key, Func<string, T> func)
    {
        int retry = 0;

        while (true)
        {
            try
            {
                return func(key);
            }
            catch (Exception ex)
            {
                _log.Error($"Redis operation: {operation} error \n {ex}");

                if (ex is RedisException)
                {
                    if (retry >= 3) 
                    {
                        throw;
                    }

                    retry++;

                }
                else
                {
                    throw;
                }
            }
        }
    }
}

包装器在 AppHost 中注册为单例实例

    public class AppHost : AppHostBase { public override void Configure(Container container){

    var redisFactory = new RedisFactory
    {
        MaxPoolSize = AppSettings.Get("MaxPoolSize", 40),
        MaxRetryAttempts = AppSettings.Get("MaxRetryAttempts", 3),
        RetryTimeout = AppSettings.Get("RetryTimeout", 5000),
        NameSpacePrefix = AppSettings.GetString("NameSpacePrefix"),
        DefaultDB = 13
    };  redisFactory.Init(); container.Register<IRedisFactory>(redisFactory);}}

Redis 工厂使用

    public class MyService
    {
        private IRedisFactory cacheClient = HostContext.TryResolve<IRedisFactory>();    

        public object SomeMethod(string key) => cacheClient.Get<object>(key);
    }

我有一个 RedisFactory::Execute 中的重试功能。我注意到第一次尝试会抛出如下异常,但第二次尝试会成功。

ServiceStack.Redis.RedisResponseException: Unknown reply on integer response: 43OK
   at ServiceStack.Redis.RedisNativeClient.ReadLong()
   at ServiceStack.Redis.RedisNativeClient.SendReceive[T](Byte[][] cmdWithBinaryArgs, Func`1 fn, Action`1 completePipelineFn, Boolean sendWithoutRead)
   at ServiceStack.Redis.RedisNativeClient.SendExpectLong(Byte[][] cmdWithBinaryArgs)
   at ServiceStack.Redis.RedisClient.ContainsKey(String key)
   at RedisLibrary.RedisFactory.<KeyExisted>b__30_0(String k)



 ServiceStack.Redis.RedisResponseException: Zero length response
   at ServiceStack.Redis.RedisNativeClient.ParseSingleLine(String r)
   at ServiceStack.Redis.RedisNativeClient.SendReceive[T](Byte[][] cmdWithBinaryArgs, Func`1 fn, Action`1 completePipelineFn, Boolean sendWithoutRead)
   at ServiceStack.Redis.RedisNativeClient.SendExpectData(Byte[][] cmdWithBinaryArgs)
   at ServiceStack.Redis.RedisClient.<>c__DisplayClass131_0`1.<Get>b__0(RedisClient r)
   at ServiceStack.Redis.RedisClient.Exec[T](Func`2 action)
   at RedisLibrary.RedisFactory.<Get>b__31_0[T](String k)

这只发生在 ContainsKey 和 Get 操作中。添加、删除或替换仍然正常工作。

需要注意的重要一点是,自从我们更改 Redis 服务器配置后,在生产中发现了这些错误,如下所示:

// Redis 服务器配置更改

  Fail over – timeout redis -cluster 5000 ->15000

  Tcp-keepalive 0 ->300

  Repl -blacklog-size 10mb -> 50mb 

我尝试在测试环境中使用与生产环境相同的 Redis 配置重现该问题,但失败了。我也一直在寻找其他类似的问题,例如在ServiceStack.Redis Client Unknown reply on integer response: 430k但没有任何帮助。

谁能帮我指出我的实现或 Redis 配置有什么问题?感谢你的帮助。

标签: .netredisservicestack

解决方案


推荐阅读