2

私の問題はこれと非常に似ています:プロトコルエラー、「データがありません」エラー、大量のシナリオでservicestack.redisを使用している間の「ゼロ長の応答」エラー

IIS で動作する C# Web アプリケーションで ServiceStack v3.9.54.0 を使用しています。Redis バージョン 2.8.17 と 3.0.501 の両方でエラーを確認できました。

私が受け取ったエラーは次のとおりです。

ServiceStack.Redis.RedisResponseException: Unexpected reply: +PONG, sPort: 65197, LastCommand: GET EX:KEY:230
at ServiceStack.Redis.RedisNativeClient.CreateResponseError(String error)
at ServiceStack.Redis.RedisNativeClient.ParseSingleLine(String r)
at ServiceStack.Redis.RedisNativeClient.ReadData()
at ServiceStack.Redis.RedisNativeClient.SendExpectData(Byte[][] cmdWithBinaryArgs)
at ServiceStack.Redis.RedisNativeClient.GetBytes(String key)
at ServiceStack.Redis.RedisNativeClient.Get(String key)

と:

ServiceStack.Redis.RedisResponseException: Unknown reply on integer response: 43PONG, sPort: 59017, LastCommand: EXISTS EX:AnKey:Cmp6
at ServiceStack.Redis.RedisNativeClient.CreateResponseError(String error)
at ServiceStack.Redis.RedisNativeClient.ReadLong()
at ServiceStack.Redis.RedisNativeClient.SendExpectLong(Byte[][] cmdWithBinaryArgs)
at ServiceStack.Redis.RedisNativeClient.Exists(String key)
at Redis.Documentos.RedisBaseType.Exists(String key)

最初に考えたのは、複数のスレッド間で Redis 接続を共有しているということでしたが、PooledRedisClientManager(Configsは接続情報を格納する静的クラスです)のシングルトン実装に問題は見られません。

public class RedisProvider
{
    public PooledRedisClientManager Pool { get; set; }
    private RedisProvider()
    {

        var srv = new List<string> { $"{Configs.Server}:{Configs.Port}" };

        Pool = new PooledRedisClientManager(srv, srv, null, 
            Configs.Database, Configs.PoolSize, Configs.PoolTimeout);
    }

    public IRedisClient GetClient()
    {
        try
        {
            var connection = (RedisClient)Pool.GetClient();
            return connection;
        }
        catch (TimeoutException)
        {
            return null;
        }
    }

    private static RedisProvider _instance;
    public static object _providerLock = new object();
    public static RedisProvider Provider
    {
        get
        {
            lock (_providerLock)
            {
                if (_instance == null)
                {
                    var instance = new RedisProvider();
                    _instance = instance;
                    return _instance;
                }
                else
                {

                    return _instance;
                }
            }
        }
    }

}

すべてのクライアントは、次のようにプールを通じて取得されます。

var redis = (RedisClient)RedisProvider.Provider.GetClient();

var が複数のスレッド間で共有されていないと確信しておりredis、私が見る限り、このコードは適切なスレッドセーフな実装を示しています...

どんな助けでも大歓迎です。


編集:私が使用しているいくつかのテクノロジによると、アプリのスタートアップ コードにアクセスすることも、usingブロックを使用することもできません。したがって、すべてのクライアントを次のようにラップします。

RedisClient redis;
try {
    redis = (RedisClient)RedisProvider.Provider.GetClient();
    // Do stuff
} finally {
    redis.Dispose();
}
4

1 に答える 1

2

このエラー メッセージは、同じ redis クライアント インスタンスが複数のスレッドで共有されていることを示しています。提供されているソース コードでは、共有されていないことを確認できません。

上記RedisProviderは、シングルトンをラップしたアクセスのより冗長なバージョンです。たとえば、次のようになります。

public static class RedisProvider
{
    public static IRedisClientManager Pool { get; set; }

    public static RedisClient GetClient()
    {
        return (RedisClient)Pool.GetClient();
    }
}

RedisManager は、アプリの起動時に 1 回だけ初期化する必要があります。

var srv = new List<string> { $"{Configs.Server}:{Configs.Port}" };
RedisProvider.Pool = new PooledRedisClientManager(srv, srv, null, 
    Configs.Database, Configs.PoolSize, Configs.PoolTimeout);

それ以降、冗長ロックはオーバーヘッドを追加するだけで、Singleton RedisManager に直接アクセスするよりもスレッドセーフな利点はありません。

クライアントをスレッドセーフに解決している間:

var redis = RedisProvider.GetClient();

返される redis クライアント インスタンスは、スレッド セーフではありません (.NET 規則に従って)。その結果、複数のスレッドで同じインスタンスを共有していないことを確認する必要があり、使用後にクライアントが破棄されることも確認する必要があります。

同じスレッドでアクセスおよび破棄されるようにするには、クライアントの使用を using ステートメントでラップする必要があります。

using (var redis = RedisProvider.GetClient())
{
    //...
} 

RedisClient を使用する必要があるたびにこれを行い、別のバックグラウンド スレッド、非同期タスク、並列化されたコードなどで同じクライアント インスタンスを共有しない場合、マルチスレッドの問題は発生しなくなります。別のスレッドで新しいクライアント インスタンスが必要な場合は、同じアクセス パターンを使用して、プールから別のクライアント インスタンスを取得 (および破棄) する必要があります。

于 2016-04-05T21:44:29.767 に答える