У нас есть следующая настройка:
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
Я предполагаю, что это прямой результат наличия тайм-аутов соединения на стороне сервера, которые не обрабатываются на клиенте. Это похоже на то, что нам действительно нужно обрабатывать таймауты на стороне клиента.