2012-10-29 9 views
18

Wir haben folgende Einstellung:Redis IOException: "Bestehende Verbindung wurde vom Remote-Host geschlossen" mit ServiceStack C# Client

Redis 2.6 auf Ubuntu Linux 12.04LTE auf einer Rackspace Cloud 8 GB-Instanz mit den folgenden Einstellungen:

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 

Unsere App-Server werden in RackSpace Managed gehostet und über öffentliche IP-Adressen mit Redis verbunden (um die Einrichtung von RackSpace Connect, einem royalen PITA, zu vermeiden), und wir bieten Sicherheit, indem wir ein Passwort für die Redis-Verbindung benötigen. Ich habe die Begrenzung der Unix-Dateideskriptoren manuell auf 10240 erhöht, max. 10k-Verbindungen sollten genügend Headroom bieten. Wie Sie aus der Einstellungsdatei oben sehen können, beschränke ich die Speicherauslastung auf 7 GB, um auch etwas RAM-Headroom zu lassen.

Wir verwenden den ServiceStack C# Redis Driver. Wir verwenden die folgenden web.config Einstellungen:

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

wir eine PooledRedisClientManager Singletons haben, erstellt einmal pro AppPool wie folgt:

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; 
    } 
} 

Und wir erwerben eine Verbindung und platziere/get-Operationen wie folgt:

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

Code scheint meistens zu funktionieren. Da wir ca. 20 AppPools und 50-100 Lese- und 50-100 Schreibclients haben, erwarten wir maximal 2000-4000 Verbindungen zum Redis-Server. In unseren Fehlerprotokollen sehen wir jedoch immer noch die folgende Ausnahme, normalerweise ein paar hundert, die zusammengebündelt sind, nichts für eine Stunde, und immer wieder, unangemessen.

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 

Wir haben mit einem Redis Server Timeout von 0 (d.h. keine Verbindung timeout), ein Timeout von 24 h experimentiert, und dazwischen, ohne Erfolg. Googeln und Stackoverflowing hat keine wirklichen Antworten gebracht, alles deutet darauf hin, dass wir mit dem Code zumindest das Richtige tun.

Unser Gefühl ist, dass wir regelmäßige anhaltende Netzwerk-Latenz Probleme Rackspace Hosted und Rackspace Cloud, die einen Block von TCP-Verbindungen zu veralten gehen. Wir könnten das möglicherweise lösen, indem wir Client-seitige Verbindungstimeouts implementieren, und die Frage wäre, ob wir auch serverseitige Timeouts benötigen. Aber das ist nur ein Gefühl, und wir sind uns nicht 100% sicher, dass wir auf dem richtigen Weg sind.

Ideen?

Edit: Ich sehe den folgenden Fehler gelegentlich auch:

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 

Ich stelle mir dies eine direkte Folge ist serverseitige Verbindung Timeouts zu haben, die auf dem Client nicht behandelt werden. Es sieht so aus, als müssten wir wirklich clientseitige Verbindungstimeouts verarbeiten.

+4

Haben Sie jemals einen guten Weg gefunden, damit umzugehen? Ich sehe das gleiche Problem beim Ausführen einer App in Azure mit Redis auf einer separaten VM. Ich denke, dass der Cloud Load Balancer die Leerlaufverbindungen zerstört, die den obigen Fehler für mich verursachen. – Jarrod

+0

Load Balancer mit Redis kann nicht wirklich verwendet werden, wir verwenden keines. Wir haben dies noch nicht wirklich gelöst - wir sehen jetzt weniger Fehler, da wir das serverseitige Verbindungszeitlimit auf 300 Sekunden gesenkt haben, aber wir sehen sie immer noch gelegentlich und haben noch keine Lösung. – Bernardo

+2

Ich sehe diesen Fehler auch bei einer Redis-Instanz, die auf derselben Box ausgeführt wird (mit MSOpenTech Build). Ich benutze 'BasicClientManager', aber unser Traffic ist viel geringer (einzelner AppPool mit niedrigen Besucherzahlen) und würde nicht mehr als eine Handvoll gleichzeitiger Verbindungen erwarten. Hast du weitere Nachforschungen angestellt? – roryf

Antwort

8

Wir denken, dass wir die Ursache nach sorgfältiger Lektüre durch die Redis Dokumentation und daran, diese Schönheit (http://redis.io/topics/persistence) gefunden:

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. 

Wir schalteten RDB Hartnäckigkeit aus, und nicht jene Verbindung fällt da gesehen.

+0

bedeutet "speichern" "und" appendonly ja "in der .conf? – sonjz

+0

für mich, sehe ich das Problem auf AWS, auch mit AOF = ON und RDB = OFF. Der seltsame Teil ist nur ein bestimmter Server wird das Problem haben. Wenn ich 6 Server im Pool habe, vielleicht 1 oder 2 haben dieses Problem durchweg, der Rest der Server sind in Ordnung. – sonjz

+0

Der Link, den Sie gepostet haben, besagt, dass das Deaktivieren von RDB deaktiviert ist und dass AOF und RDB eventuell zu einer einzelnen Persistenzschicht zusammengeführt werden können. Also, das kann keine langfristige Lösung sein, und das Ändern der Server-Timeout scheint hacky. Ich bin überrascht, dass es zu diesem Thema keine weiteren Lösungen gibt. – Steven

1

Es scheint, dass das Festlegen des Server-Timeouts auf 300 von 0 die Verbindungen bei einem Massenmangel verringert hat. Immer noch einige schlechte Verbindungen, aber das liegt möglicherweise daran, dass der PooledRedisClientManager den Verbindungsstatus für GetInActiveWriteClient(), der von GetClient() aufgerufen wird, nicht richtig überprüft.

Verwandte Themen