18

次の設定があります。

以下の設定で、RackspaceCloud 8GB インスタンス上の Ubuntu Linux 12.04LTE 上の Redis 2.6:

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 でホストされ、パブリック IP を介して Redis に接続します (ロイヤル PITA である RackSpace Connect をセットアップする必要がないようにするため)。Redis 接続にパスワードを要求することで、ある程度のセキュリティを提供します。UNIX ファイル記述子の制限を手動で 10240 に増やしました。最大 10,000 の接続で十分なヘッドルームが提供されるはずです。上記の設定ファイルからわかるように、メモリの使用量を 7GB に制限して、RAM のヘッドルームも確保しています。

ServiceStack C# Redis ドライバーを使用します。次の web.config 設定を使用します。

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

次のように、AppPool ごとに 1 回作成される PooledRedisClientManager シングルトンがあります。

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 の読み取りおよび 50 ~ 100 の書き込みクライアントがあることを考えると、Redis サーバーへの接続は最大で 2000 ~ 4000 になると予想されます。ただし、エラー ログに次の例外が表示され続けます。通常は数百個の例外がまとめられ、1 時間何も表示されず、何度も何度もうんざりします。

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 サーバーのタイムアウトを 0 (つまり、接続タイムアウトなし)、24 時間のタイムアウト、およびその間の値で実験しましたが、うまくいきませんでした。グーグルとスタックオーバーフローは本当の答えをもたらしませんでした.少なくともコードで正しいことをしていることをすべてが示しているようです.

私たちの感覚では、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

これは、クライアントで処理されないサーバー側の接続タイムアウトの直接的な結果だと思います。クライアント側の接続タイムアウトを処理する必要があるようです。

4

2 に答える 2

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.

RDB の永続性をオフにして以来、接続が切断されたことはありません。

于 2013-11-20T15:47:19.300 に答える
1

サーバーのタイムアウトを 0 から 300 に設定すると、接続が一斉に失敗する問題が緩和されたようです。まだいくつかの接続不良が見られますが、これは PooledRedisClientManager が GetClient() から呼び出される GetInActiveWriteClient () の接続状態を適切にチェックしていないことが原因である可能性があります。

于 2012-11-01T15:12:56.703 に答える