Redis IOException:"使用ServiceStack C#客户端强制关闭远程主机的现有连接"

Ber*_*rdo 18 .net c# iis redis servicestack

我们有以下设置:

Redis的在Ubuntu Linux 12.04LTE 2.6以下设置一个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
Run Code Online (Sandbox Code Playgroud)

我们的App服务器托管在RackSpace托管中并通过公共IP连接到Redis(以避免必须设置RackSpace Connect,这是一个皇家PITA),我们通过要求Redis连接的密码来提供一些安全性.我手动将unix文件描述符限制增加到10240,最多10k连接应该提供足够的空间.从上面的设置文件中可以看出,我将内存使用量限制为7GB,以便留下一些RAM空间.

我们使用ServiceStack C#Redis驱动程序.我们使用以下web.config设置:

<RedisConfig suffix="">
  <Primary password="PASSWORD" host="HOST" port="6379"  maxReadPoolSize="50" maxWritePoolSize="50"/>
</RedisConfig>  
Run Code Online (Sandbox Code Playgroud)

我们有一个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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我们获取连接并执行如下的put/get操作:

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

代码似乎主要起作用.鉴于我们有大约20个AppPools和50-100个读取客户端以及50-100个写入客户端,我们希望最多可以连接到Redis服务器的2000-4000个连接.但是,我们在错误日志中看到以下异常,通常是几百个聚集在一起,一小时都没有,一遍又一遍,令人作呕.

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
Run Code Online (Sandbox Code Playgroud)

我们已经尝试过Redis服务器超时为0(即没有连接超时),超时为24小时,介于两者之间,没有运气.谷歌搜索和Stackoverflowing没有带来真正的答案,一切似乎都指向我们至少使用代码做正确的事情.

我们的感觉是,我们在Rackspace Hosted和Rackspace Cloud之间经常出现持续的网络延迟问题,这会导致TCP连接块过时.我们可以通过实现客户端连接超时来解决这个问题,问题是我们是否还需要服务器端超时.但这只是一种感觉,我们并不是百分之百确定我们是在正确的轨道上.

想法?

编辑:我偶尔也会看到以下错误:

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
Run Code Online (Sandbox Code Playgroud)

我想这是服务器端连接超时无法在客户端上处理的直接结果.看起来我们真的需要处理客户端连接超时.

Ber*_*rdo 9

我们认为在仔细阅读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.
Run Code Online (Sandbox Code Playgroud)

我们关闭了RDB持久性,并且从那以后没有看到那些连接丢失.