Подтвердить что ты не робот

Redis IOException: "Существующее соединение, принудительно закрытое удаленным хостом" с использованием ServiceStack С# client

У нас есть следующая настройка:

Redis 2.6 на Ubuntu Linux 12.04LTE на экземпляре RackspaceCloud 8GB со следующими настройками:

daemonize yes
pidfile /var/run/redis_6379.pid

port 6379

timeout 300

loglevel notice
logfile /var/log/redis_6379.log

databases 16

save 900 1
save 300 10
save 60 10000

rdbcompression yes
dbfilename dump.rdb
dir /var/redis/6379

requirepass PASSWORD

maxclients 10000

maxmemory 7gb
maxmemory-policy allkeys-lru
maxmemory-samples 3

appendonly no

slowlog-log-slower-than 10000
slowlog-max-len 128

activerehashing yes

Наши серверы приложений размещаются в RackSpace Managed и подключаются к Redis через общедоступный IP-адрес (чтобы избежать необходимости устанавливать RackSpace Connect, который является королевским PITA), и мы обеспечиваем некоторую безопасность, требуя пароль для соединения Redis. Я вручную увеличил ограничения дескриптора файла unix до 10240, максимальные 10k соединений должны обеспечить достаточный запас высоты. Как видно из приведенного выше файла настроек, я ограничиваю использование памяти до 7 ГБ, чтобы оставить некоторый объем памяти.

Мы используем драйвер ServiceStack С# Redis. Мы используем следующие настройки web.config:

<RedisConfig suffix="">
  <Primary password="PASSWORD" host="HOST" port="6379"  maxReadPoolSize="50" maxWritePoolSize="50"/>
</RedisConfig>  

У нас есть Singleton PooledRedisClientManager, созданный один раз для AppPool следующим образом:

private static PooledRedisClientManager _clientManager;
public static PooledRedisClientManager ClientManager
{
    get
    {
        if (_clientManager == null)
        {
            try
            {
                var poolConfig = new RedisClientManagerConfig
                {
                    MaxReadPoolSize = RedisConfig.Config.Primary.MaxReadPoolSize,
                    MaxWritePoolSize = RedisConfig.Config.Primary.MaxWritePoolSize,
                };

                _clientManager = new PooledRedisClientManager(new List<string>() { RedisConfig.Config.Primary.ToHost() }, null, poolConfig);
            }
            catch (Exception e)
            {
                log.Fatal("Could not spin up Redis", e);
                CacheFailed = DateTime.Now;
            }
        }
        return _clientManager;
    }
}

И мы приобретаем соединение и делаем put/get следующие операции:

    using (var client = ClientManager.GetClient())
    {
        client.Set<T>(region + key, value);
    }

Код, похоже, работает в основном. Учитывая, что у нас есть ~ 20 AppPools и 50-100 read и 50-100 клиентов записи, мы ожидаем 2000-4000 подключений к серверу Redis максимум. Однако в наших журналах ошибок мы наблюдаем следующее исключение, как правило, несколько сотен из них сгруппированы вместе, ничто в течение часа, и снова, объявление nauseum.

System.IO.IOException: Unable to read data from the transport connection:
An existing connection was forcibly closed by the remote host.
---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host at
System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) at
System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
--- End of inner exception stack trace
- at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size) at System.IO.BufferedStream.ReadByte() at
ServiceStack.Redis.RedisNativeClient.ReadLine() in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient_Utils.cs:line 85 at
ServiceStack.Redis.RedisNativeClient.SendExpectData(Byte[][] cmdWithBinaryArgs) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient_Utils.cs:line 355 at
ServiceStack.Redis.RedisNativeClient.GetBytes(String key) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient.cs:line 404 at ServiceStack.Redis.RedisClient.GetValue(String key) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisClient.cs:line 185 at ServiceStack.Redis.RedisClient.Get[T](String key) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisClient.ICacheClient.cs:line 32 at DataPeaks.NoSQL.RedisCacheClient.Get[T](String key) in c:\dev\base\branches\currentversion\DataPeaks\DataPeaks.NoSQL\RedisCacheClient.cs:line 96

Мы экспериментировали с Redis Server Timeout 0 (т.е. NO time timeout), тайм-аутом 24 часа и между ними, без везения. Googling и Stackoverflowing не дали реальных ответов, все, кажется, указывает на то, что мы делаем правильные вещи с кодом, по крайней мере.

Наше ощущение состоит в том, что мы получаем регулярные постоянные проблемы с задержкой в ​​сети, связанные с Rackspace Hosted и Rackspace Cloud, которые заставляют блок TCP-соединений устаревать. Мы могли бы решить это, установив тайм-ауты подключения сторонних клиентов, и вопрос будет заключаться в том, нужны ли нам тайм-ауты на стороне сервера. Но это просто чувство, и мы не на 100% уверены, что мы на правильном пути.

Идеи?

Изменить: Иногда я также вижу следующую ошибку:

ServiceStack.Redis.RedisException: Unable to Connect: sPort: 65025 ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host at System.Net.Sockets.Socket.Send(IList`1 buffers, SocketFlags socketFlags) at ServiceStack.Redis.RedisNativeClient.FlushSendBuffer() in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient_Utils.cs:line 273 at ServiceStack.Redis.RedisNativeClient.SendCommand(Byte[][] cmdWithBinaryArgs) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient_Utils.cs:line 203 --- End of inner exception stack trace --- at ServiceStack.Redis.RedisNativeClient.CreateConnectionError() in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient_Utils.cs:line 165 at ServiceStack.Redis.RedisNativeClient.SendExpectData(Byte[][] cmdWithBinaryArgs) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient_Utils.cs:line 355 at ServiceStack.Redis.RedisNativeClient.GetBytes(String key) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisNativeClient.cs:line 404 at ServiceStack.Redis.RedisClient.GetValue(String key) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisClient.cs:line 185 at ServiceStack.Redis.RedisClient.Get[T](String key) in C:\src\ServiceStack.Redis\src\ServiceStack.Redis\RedisClient.ICacheClient.cs:line 32 at DataPeaks.NoSQL.RedisCacheClient.Get[T](String key) in c:\dev\base\branches\currentversion\DataPeaks\DataPeaks.NoSQL\RedisCacheClient.cs:line 96

Я предполагаю, что это прямой результат наличия тайм-аутов соединения на стороне сервера, которые не обрабатываются на клиенте. Это похоже на то, что нам действительно нужно обрабатывать таймауты на стороне клиента.

4b9b3361

Ответ 1

Мы считаем, что мы нашли основную причину после тщательного изучения документации Redis и поиска этой красоты (http://redis.io/topics/persistence):

RDB needs to fork() often in order to persist on disk using a child process.
Fork() can be time consuming if the dataset is big, and may result in Redis
to stop serving clients for some millisecond or even for one second if the
dataset is very big and the CPU performance not great. AOF also needs to fork()
but you can tune how often you want to rewrite your logs without any trade-off
on durability.

Мы отключили постоянство RDB и не увидели, что эти соединения упали с тех пор.

Ответ 2

Кажется, что установка тайм-аута сервера до 300 из 0 облегчала соединение, не справляющееся с проблемой en-masse. Все еще видя некоторые плохие соединения, но это может быть связано с тем, что PooledRedisClientManager неправильно проверяет состояние соединения для GetInActiveWriteClient(), который вызывается из GetClient().