次の設定があります。
以下の設定で、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
これは、クライアントで処理されないサーバー側の接続タイムアウトの直接的な結果だと思います。クライアント側の接続タイムアウトを処理する必要があるようです。