Mamy następującą konfigurację:Redis IOException: "Istniejące połączenie przymusowo zamknięte przez zdalnego hosta" za pomocą ServiceStack C# klienta
Redis 2.6 na Ubuntu Linux 12.04LTE na przykład RackspaceCloud 8GB z następującymi ustawieniami:
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
Nasze serwery aplikacji są hostowane w RackSpace Zarządzane i łączą się z Redis za pośrednictwem publicznego adresu IP (aby uniknąć konieczności konfigurowania RackSpace Connect, która jest królewską PITA) i zapewniamy pewne zabezpieczenia wymagając hasła do połączenia Redis. Ręcznie zwiększyłem limit deskryptorów plików uniksowych do 10240, maks. 10 000 połączeń powinno zapewnić wystarczającą ilość miejsca. Jak widać z powyższego pliku ustawień, ograniczam użycie pamięci do 7 GB, aby zostawić trochę miejsca na pamięci RAM.
Używamy sterownika ServiceStack C# Redis. Używamy następujących ustawień web.config:
<RedisConfig suffix="">
<Primary password="PASSWORD" host="HOST" port="6379" maxReadPoolSize="50" maxWritePoolSize="50"/>
</RedisConfig>
Mamy singleton PooledRedisClientManager, stworzony raz na AppPool następująco:
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;
}
}
A my nabyć połączenie i kładź/dostać operacji w następujący sposób:
using (var client = ClientManager.GetClient())
{
client.Set<T>(region + key, value);
}
Kod wydaje się działać głównie. Biorąc pod uwagę, że mamy ~ 20 AppPools i 50-100 read i 50-100 write clients, oczekujemy maksymalnie 2000-4000 połączeń z serwerem Redis. Jednak w naszych dziennikach błędów widzimy następujący wyjątek, zwykle kilkaset tych, które są ze sobą połączone, nic przez godzinę, i znowu, na nause.
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
W eksperymentowali z Redis serwera Timeout 0 (czyli nie jest limit czasu połączenia), a czas oczekiwania na 24 godzin, a pomiędzy nimi, bez powodzenia. Googling i Stackoverflowing nie przyniosły prawdziwych odpowiedzi, wszystko wskazuje na to, że postępujemy właściwie z kodem.
Nasze odczucie jest takie, że regularnie występują problemy z opóźnieniami w sieci, między innymi Rackspace Hosted i Rackspace Cloud, które powodują, że blok połączeń TCP przestaje działać. Możemy rozwiązać ten problem, wprowadzając limity czasu połączenia po stronie klienta, a pytanie brzmi, czy potrzebowalibyśmy również limitu czasu po stronie serwera. Ale to tylko uczucie, a my nie jesteśmy w 100% pewni, że jesteśmy na dobrej drodze.
Pomysły?
Edit: I od czasu do czasu zobaczyć następujący komunikat o błędzie, a także:
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
sobie wyobrazić, że jest to bezpośrednim wynikiem konieczności po stronie serwera limity czasu połączeń, które nie są obsługiwane na kliencie. Wygląda na to, że naprawdę musimy obsługiwać limity czasu połączenia po stronie klienta.
Czy kiedykolwiek znalazłeś dobry sposób na poradzenie sobie z tym? Widzę ten sam problem z uruchomieniem aplikacji na Azure z Redis na oddzielnej maszynie wirtualnej. Myślę, że wyrównywacz obciążenia w chmurze zabija bezczynne połączenia powodując powyższy błąd. – Jarrod
Nie można tak naprawdę korzystać z systemu równoważenia obciążenia w programie Redis, ponieważ go nie używamy. Naprawdę nie rozwiązaliśmy tego problemu - teraz widzimy mniej błędów, ponieważ zmniejszyliśmy czas oczekiwania na połączenie z serwerem do 300 sekund, ale wciąż widzimy je sporadycznie i nie mamy jeszcze rozwiązania. – Bernardo
Widzę również ten błąd z wystąpieniem redis działającym w tym samym polu (przy użyciu kompilacji MSOpenTech). Używam 'BasicClientManager', ale nasz ruch jest znacznie mniejszy (pojedynczy AppPool z niskimi liczbami gości), więc nie spodziewalibyśmy się więcej niż kilka współbieżnych połączeń. Czy masz dalsze dochodzenie? – roryf