web-dev-qa-db-ja.com

Azure RedisでStackExchange.Redisが使用できないほど遅いか、タイムアウトエラーがスローされる

既存のAzure In-Roleキャッシュの使用をすべてRedisに移行し、Azure RedisプレビューとStackExchange.Redisライブラリ( https://github.com/StackExchange/StackExchangeを使用することにしました.Redis )。問題なくすべてのコードを作成しましたが、実行すると絶対に使用できなくなり、タイムアウトエラーが常に発生します(タイムアウト期間は15秒に設定されています)。

以下は、Redis接続を設定し、簡単な操作に使用する方法に関連するコードです。

    private static ConnectionMultiplexer _cacheService;
    private static IDatabase _database;
    private static object _lock = new object();

    private void Initialize()
    {
        if (_cacheService == null)
        {
            lock (_lock)
            {
                if (_cacheService == null)
                {
                    var options = new ConfigurationOptions();
                    options.EndPoints.Add("{my url}", 6380);
                    options.Ssl = true;
                    options.Password = "my password";
                    // needed for FLUSHDB command
                    options.AllowAdmin = true;

                    // necessary?
                    options.KeepAlive = 30;
                    options.ConnectTimeout = 15000;
                    options.SyncTimeout = 15000;

                    int database = 0;

                    _cacheService = ConnectionMultiplexer.Connect(options);
                    _database = _cacheService.GetDatabase(database);
                }
            }
        }

    }

    public void Set(string key, object data, TimeSpan? expiry = null)
    {
        if (_database != null)
        {
            _database.Set(key, data, expiry: expiry);
        }
    }

    public object Get(string key)
    {
        if (_database != null)
        {
            return _database.Get(key);
        }
        return null;
    }

GetやSetなどの非常に単純なコマンドを実行すると、タイムアウトになるか、完了するまでに5〜10秒かかることがよくあります。データベースから実際のデータを実際にフェッチするよりもかなり遅い場合、キャッシュとして使用するという目的全体を無効にしているようです:)

私は明らかに間違ったことをしていますか?

Edit:何かに光を当てる場合に備えて、(Redis Desktop Managerを使用して)サーバーから取得した統計情報を以下に示します。

Server
redis_version:2.8.12
redis_mode:standalone
os:Windows  
Arch_bits:64
multiplexing_api:winsock_IOCP
gcc_version:0.0.0
process_id:2876

tcp_port:6379
uptime_in_seconds:109909
uptime_in_days:1
hz:10
lru_clock:16072421
config_file:C:\Resources\directory\xxxx.Kernel.localStore\1\redis_2092_port6379.conf

Clients
connected_clients:5
client_longest_output_list:0
client_biggest_input_buf:0
client_total_writes_outstanding:0
client_total_sent_bytes_outstanding:0
blocked_clients:0

Memory
used_memory:4256488
used_memory_human:4.06M
used_memory_rss:67108864
used_memory_rss_human:64.00M
used_memory_peak:5469760
used_memory_peak_human:5.22M
used_memory_lua:33792
mem_fragmentation_ratio:15.77
mem_allocator:dlmalloc-2.8

Persistence
loading:0
rdb_changes_since_last_save:72465
rdb_bgsave_in_progress:0
rdb_last_save_time:1408471440
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

Stats
total_connections_received:25266
total_commands_processed:123389
instantaneous_ops_per_sec:10
bytes_received_per_sec:275
bytes_sent_per_sec:65
bytes_received_per_sec_human:

Edit 2:Get/Setに使用している拡張メソッドは次のとおりです。これらは、オブジェクトをJSONに変換して呼び出すだけの非常に単純なメソッドです。 StringSet

    public static object Get(this IDatabase cache, string key)
    {
        return DeserializeJson<object>(cache.StringGet(key));
    }

    public static void Set(this IDatabase cache, string key, object value, TimeSpan? expiry = null)
    {
        cache.StringSet(key, SerializeJson(value), expiry: expiry);
    }

Edit 3:ここにいくつかのエラーメッセージの例があります:

    A first chance exception of type 'System.TimeoutException' occurred in StackExchange.Redis.dll
    Timeout performing GET MyCachedList, inst: 11, queue: 1, qu=1, qs=0, qc=0, wr=0/1, in=0/0

    A first chance exception of type 'System.TimeoutException' occurred in StackExchange.Redis.dll
    Timeout performing GET MyCachedList, inst: 1, queue: 97, qu=0, qs=97, qc=0, wr=0/0, in=3568/0
29
user2719100

Azure Redis Cacheドキュメント からの推奨パターンは次のとおりです。

private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => {
    return ConnectionMultiplexer.Connect("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
});

public static ConnectionMultiplexer Connection {
    get {
        return lazyConnection.Value;
    }
}

いくつかの重要なポイント:

  • Lazy <T>を使用して、スレッドセーフな初期化を処理します。
  • これは、「abortConnect = false」を設定します。つまり、最初の接続試行が失敗した場合、ConnectionMultiplexerは例外をスローするのではなく、サイレントでバックグラウンドで再試行します。
  • ConnectionMultiplexerは接続が切断されるとバックグラウンドで自動的に再試行するため、IsConnectedプロパティをnotチェックします。
22
Mike Harder

私は同様の問題を抱えていました。 Redisキャッシュは異常に低速でしたが、間違いなくキャッシュしていました。場合によっては、ページの読み込みに20〜40秒かかりました。

キャッシュサーバーがサイトとは異なる場所にあることに気付きました。 Webサイトと同じ場所にあるようにキャッシュサーバーを更新し、すべてが期待どおりに動作するようになりました。

同じページが4〜6秒で読み込まれます。

これらの問題を抱えている他の人に幸運を。

5
christo8989

問題は、接続オブジェクトの作成方法と使用方法です。最初は正確な問題に直面していましたが、すべてのWeb要求で単一の接続オブジェクトが使用されるように修正されました。そして、オブジェクトが正常に再作成されるように、セッション開始時にnullまたは接続されていることを確認します。それは問題を修正しました。

注:また、Redis Cacheインスタンスが実行されているAzureのゾーンと、Webサーバーが存在するゾーンも確認します。両方を同じゾーンに維持することをお勧めします

Global.ascx.csファイル内

public static ConnectionMultiplexer RedisConnection;
public static IDatabase RedisCacheDb;

protected void Session_Start(object sender, EventArgs e)
    {
        if (ConfigurationManager.ConnectionStrings["RedisCache"] != null)
        {
            if (RedisConnection == null || !RedisConnection.IsConnected)
            {
                RedisConnection = ConnectionMultiplexer.Connect(ConfigurationManager.ConnectionStrings["RedisCache"].ConnectionString);
            }
            RedisCacheDb = RedisConnection.GetDatabase();
        }
    }
4

私たちの場合、問題はSSL接続を使用している場合です。デスクトップマネージャーが非SSLポートで実行されていることを示していますが、コードはSSLを使用しています。

SSLなしのAzure redisの簡単なベンチマークは、LRANGEコマンド(.netおよびStackExchange.Redisを使用)で約80kの値を取得することを基本的に理解しています。 SSLを使用する場合、同じクエリには27秒かかります。

WebApp:標準S2

Redis:標準1 GB

編集:SLOWLOGを確認すると、Redis自体は実際にスローログにヒットするようです。行を取得するのに14msほどかかりますが、SSLが有効な場合の実際の転送とはかけ離れています。 RedisとWeb Appsの間に何らかのセキュリティを確保するため、最終的にプレミアムRedisを作成しました。

3
Crypth

私の場合はうまくいきました。 SyncTimeoutを増やすことを忘れないでください。デフォルトは1秒です。

private static Lazy<ConnectionMultiplexer> ConnectionMultiplexerItem = new Lazy<ConnectionMultiplexer>(() =>
{
    var redisConfig = ConfigurationOptions.Parse("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");

    redisConfig.SyncTimeout = 3000;

    return ConnectionMultiplexer.Connect(redisConfig);
});

Azure Redis CacheとクライアントがAzureの同じリージョンにあるかどうかを確認します。たとえば、キャッシュが米国東部にあるがクライアントが米国西部にあり、リクエストがsynctimeout時間で完了しない場合にタイムアウトが発生する場合があります。または、ローカル開発machinexからデバッグしている場合にタイムアウトが発生する場合があります。キャッシュを同じAzureリージョンのクライアントに置くことを強くお勧めします。リージョン間コールを実行するシナリオがある場合は、synctimeoutをより高い値に設定する必要があります。

詳細を読む: https://Azure.Microsoft.com/en-us/blog/investigating-timeout-exceptions-in-stackexchange-redis-for-Azure-redis-cache/