servicestack.redis,servicestack-bsd,C#,Multithreading,Redis,servicestack.redis,Servicestack Bsd" /> servicestack.redis,servicestack-bsd,C#,Multithreading,Redis,servicestack.redis,Servicestack Bsd" />

C# 使用ServiceStack.Redis对大容量场景的意外回复

C# 使用ServiceStack.Redis对大容量场景的意外回复,c#,multithreading,redis,servicestack.redis,servicestack-bsd,C#,Multithreading,Redis,servicestack.redis,Servicestack Bsd,我的问题与此非常相似: 我在一个处理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.Cr

我的问题与此非常相似:

我在一个处理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)
以及:

我想到的第一件事是,我正在跨多个线程共享Redis连接,但我看不到我的
PooledRedisClientManager
的单例实现上的问题(
Configs
是一个存储连接信息的静态类):

我确信,
redis
var不会在多个线程之间共享,并且,就我所见,这段代码显示了一个正确的线程安全实现

任何帮助都将不胜感激


编辑:根据我使用的一些技术,我无法访问应用程序启动代码,也无法使用
块使用
。因此,我将所有客户机包装成这样:

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

此错误消息表示同一个redis客户端实例正在多个线程之间共享,提供的源代码未提供任何验证

上述
RedisProvider
只是围绕单例的access的更详细版本,例如:

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

    public static RedisClient GetClient()
    {
        return (RedisClient)Pool.GetClient();
    }
}
RedisManager只需在应用程序启动时初始化一次:

var srv = new List<string> { $"{Configs.Server}:{Configs.Port}" };
RedisProvider.Pool = new PooledRedisClientManager(srv, srv, null, 
    Configs.Database, Configs.PoolSize, Configs.PoolTimeout);
返回的redis客户端实例不是线程安全的(根据.NET约定)。因此,您需要确保没有跨多个线程共享同一实例,还需要确保客户端在使用后被释放

要确保在同一线程中访问和释放它,您应该将客户端使用情况包装在using语句中:

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

如果您在需要使用RedisClient时执行此操作,并且不在不同的后台线程、异步任务、并行化代码等中共享同一个客户端实例,那么您就不会再有任何多线程问题。当您需要不同线程中的新客户端实例时,您应该使用相同的访问模式,并从池中检索(和处置)一个单独的客户端实例。

谢谢您的回答。请看一下我的编辑。虽然我理解并同意您的建议,但我是否可以假设singleton实现是线程安全的,而我将客户机放在try finally块中,并确保不跨多个线程共享它?除了在多线程中共享客户机之外,还有其他可能的问题原因吗?@DineiA.Rockenbach此错误表示没有收到预期的答复,这一直表明使用同一客户机实例在不同线程中发送新命令。我无法判断问题在您的代码库中的位置,但如果您可以创建一个显示错误的小型独立回购协议(例如,在GitHub上),我可以让您知道问题所在。@mythz静态
IRediclientsManager
应该在哪里处理?我根本不使用DI框架。我正在创建和处理每个调用的
IRedisClientsManager
(使用
语句)以及
IRedisClient
。在客户端管理器中不使用单例可以吗?@Alisson它需要是单例才能共享客户端,您不需要处理AppDomain回收时它将处理的单例。@mythz这很有意义。谢谢你的回复,upv,因为你的回答也解决了我的问题。
public static class RedisProvider
{
    public static IRedisClientManager Pool { get; set; }

    public static RedisClient GetClient()
    {
        return (RedisClient)Pool.GetClient();
    }
}
var srv = new List<string> { $"{Configs.Server}:{Configs.Port}" };
RedisProvider.Pool = new PooledRedisClientManager(srv, srv, null, 
    Configs.Database, Configs.PoolSize, Configs.PoolTimeout);
var redis = RedisProvider.GetClient();
using (var redis = RedisProvider.GetClient())
{
    //...
}