ServiceStack.Redis 多线程冲突
ServiceStack.Redis multi-threading collision
我遇到了 ServiceStack.Redis 多线程冲突。我有一种 ServiceStack 的非标准用例,我在一个 "FallbackRoute" 中捕获所有路由。这意味着一条路线只有一项服务。这是 DTO:
[FallbackRoute("/{Path*}")]
public class S3Request : IRequiresRequestStream{
public string Path{ get; set; }
public Stream RequestStream { get; set; }
}
服务是:
public class S3Service : Service
{
public RedisUsersCredentials RedisUsersCredentials { get; set; }
// S3 Request Mutual Exclusion Objects:
static readonly object syncBucket = new object();
static readonly object syncObject = new object();
public object Get(S3Request request){
IRedisClient Redis = RedisUsersCredentials.RedisClient;
// Do a bunch of stuff with Redis
}
public object Put(S3Request request){
IRedisClient Redis = RedisUsersCredentials.RedisClient;
// Do a bunch of stuff with Redis
}
然后在 AppHost.cs 配置块中我自动连接了 redis:
container.Register<ServiceStack.Redis.IRedisClientsManager>(c =>
new ServiceStack.Redis.BasicRedisClientManager("localhost:6379"));
container.RegisterAutoWired<RedisUsersCredentials>();
我是从关于如何正确使用多线程 BasicRedisClientManager 的帖子中得到的。但我得到像这样的例外:
multi-request: 581, sPort: 40900, LastCommand: SMEMBERS TenantOwnerSet:s3devel</Message><StackTrace>[S3Request: 4/1/2015 6:36:50 PM]:
[REQUEST: {}]
ServiceStack.Redis.RedisResponseException: Unknown reply on multi-request: 581, sPort: 40900, LastCommand: SMEMBERS TenantOwnerSet:s3devel
at ServiceStack.Redis.RedisNativeClient.CreateResponseError (string) <IL 0x0003a, 0x00153>
at ServiceStack.Redis.RedisNativeClient.ReadMultiData () <IL 0x000e5, 0x0060f>
at ServiceStack.Redis.RedisNativeClient.SendExpectMultiData (byte[][]) <IL 0x00037, 0x001db>
at ServiceStack.Redis.RedisNativeClient.SMembers (string) <IL 0x0001a, 0x000cf>
at ServiceStack.Redis.Generic.RedisTypedClient`1<string>.GetAllItemsFromSet (ServiceStack.Redis.Generic.IRedisSet`1<string>) <0x00083>
at ServiceStack.Redis.Generic.RedisClientSet`1<string>.GetAll () <0x0006f>
at S3OL.TenantManager.getTenantOwner () [0x0001e] in /home/admin/devgit/ols3/mono/src/TenantManager.cs:87
at S3OL.TenantManager..ctor (ServiceStack.Redis.IRedisClient,string) [0x00084] in /home/admin/devgit/ols3/mono/src/TenantManager.cs:60
at S3OL.BucketManager..ctor (ServiceStack.Redis.IRedisClient,string,string) [0x00016] in /home/admin/devgit/ols3/mono/src/BucketManager.cs:120
at S3OL.S3Service.Delete (S3OL.S3Request) [0x00195] in /home/admin/devgit/ols3/mono/Interface.cs:570
at (wrapper dynamic-method) object.lambda_method (System.Runtime.CompilerServices.Closure,object,object) <IL 0x0000c, 0x000a3>
at ServiceStack.Host.ServiceRunner`1<S3OL.S3Request>.Execute (ServiceStack.Web.IRequest,object,S3OL.S3Request) <0x00642>
</StackTrace><Errors /></ResponseStatus></ErrorResponse>
只有当我有多个客户时才会发生。我可以用一个客户反复打它,而且速度非常快。如果我同时添加一个客户端,它会死于这些 Redis 异常之一。
IRedisClient
实例不是线程安全的,不应在多个线程之间共享。看起来您正在重复使用同一个 RedisClient 实例:
IRedisClient Redis = RedisUsersCredentials.RedisClient;
您应该为每个请求访问和发布一个新的 IRedisClient
,您可以通过访问服务中的 Redis 客户端来执行此操作,例如:
public object Put(S3Request request){
base.Redis;
// Do a bunch of stuff with Redis
}
之所以可行,是因为 creates a new instance for that Service 如果它不存在:
private IRedisClient redis;
public virtual IRedisClient Redis
{
get { return redis ?? (redis = RedisManager.GetClient()); }
}
服务执行后 gets disposed if it's created:
public virtual void Dispose()
{
...
if (redis != null)
redis.Dispose();
}
如果您不想使用从配置的 IRedisClientsManager
创建的 base.Redis
实例,那么您必须在您的服务中创建您自己的实例并自行处理它,您通常会这样做在 using 块中创建和处理,例如:
public object Get(S3Request request)
{
using (var redis = RedisUsersCredentials.CreateRedisClient())
{
// Do a bunch of stuff with Redis
}
}
每次您的服务将使用未被任何其他线程共享的新客户端实例时,通过从 IRedisClientsManager
解析和释放新的 IRedisClient
。
我遇到了 ServiceStack.Redis 多线程冲突。我有一种 ServiceStack 的非标准用例,我在一个 "FallbackRoute" 中捕获所有路由。这意味着一条路线只有一项服务。这是 DTO:
[FallbackRoute("/{Path*}")]
public class S3Request : IRequiresRequestStream{
public string Path{ get; set; }
public Stream RequestStream { get; set; }
}
服务是:
public class S3Service : Service
{
public RedisUsersCredentials RedisUsersCredentials { get; set; }
// S3 Request Mutual Exclusion Objects:
static readonly object syncBucket = new object();
static readonly object syncObject = new object();
public object Get(S3Request request){
IRedisClient Redis = RedisUsersCredentials.RedisClient;
// Do a bunch of stuff with Redis
}
public object Put(S3Request request){
IRedisClient Redis = RedisUsersCredentials.RedisClient;
// Do a bunch of stuff with Redis
}
然后在 AppHost.cs 配置块中我自动连接了 redis:
container.Register<ServiceStack.Redis.IRedisClientsManager>(c =>
new ServiceStack.Redis.BasicRedisClientManager("localhost:6379"));
container.RegisterAutoWired<RedisUsersCredentials>();
我是从关于如何正确使用多线程 BasicRedisClientManager 的帖子中得到的。但我得到像这样的例外:
multi-request: 581, sPort: 40900, LastCommand: SMEMBERS TenantOwnerSet:s3devel</Message><StackTrace>[S3Request: 4/1/2015 6:36:50 PM]:
[REQUEST: {}]
ServiceStack.Redis.RedisResponseException: Unknown reply on multi-request: 581, sPort: 40900, LastCommand: SMEMBERS TenantOwnerSet:s3devel
at ServiceStack.Redis.RedisNativeClient.CreateResponseError (string) <IL 0x0003a, 0x00153>
at ServiceStack.Redis.RedisNativeClient.ReadMultiData () <IL 0x000e5, 0x0060f>
at ServiceStack.Redis.RedisNativeClient.SendExpectMultiData (byte[][]) <IL 0x00037, 0x001db>
at ServiceStack.Redis.RedisNativeClient.SMembers (string) <IL 0x0001a, 0x000cf>
at ServiceStack.Redis.Generic.RedisTypedClient`1<string>.GetAllItemsFromSet (ServiceStack.Redis.Generic.IRedisSet`1<string>) <0x00083>
at ServiceStack.Redis.Generic.RedisClientSet`1<string>.GetAll () <0x0006f>
at S3OL.TenantManager.getTenantOwner () [0x0001e] in /home/admin/devgit/ols3/mono/src/TenantManager.cs:87
at S3OL.TenantManager..ctor (ServiceStack.Redis.IRedisClient,string) [0x00084] in /home/admin/devgit/ols3/mono/src/TenantManager.cs:60
at S3OL.BucketManager..ctor (ServiceStack.Redis.IRedisClient,string,string) [0x00016] in /home/admin/devgit/ols3/mono/src/BucketManager.cs:120
at S3OL.S3Service.Delete (S3OL.S3Request) [0x00195] in /home/admin/devgit/ols3/mono/Interface.cs:570
at (wrapper dynamic-method) object.lambda_method (System.Runtime.CompilerServices.Closure,object,object) <IL 0x0000c, 0x000a3>
at ServiceStack.Host.ServiceRunner`1<S3OL.S3Request>.Execute (ServiceStack.Web.IRequest,object,S3OL.S3Request) <0x00642>
</StackTrace><Errors /></ResponseStatus></ErrorResponse>
只有当我有多个客户时才会发生。我可以用一个客户反复打它,而且速度非常快。如果我同时添加一个客户端,它会死于这些 Redis 异常之一。
IRedisClient
实例不是线程安全的,不应在多个线程之间共享。看起来您正在重复使用同一个 RedisClient 实例:
IRedisClient Redis = RedisUsersCredentials.RedisClient;
您应该为每个请求访问和发布一个新的 IRedisClient
,您可以通过访问服务中的 Redis 客户端来执行此操作,例如:
public object Put(S3Request request){
base.Redis;
// Do a bunch of stuff with Redis
}
之所以可行,是因为 creates a new instance for that Service 如果它不存在:
private IRedisClient redis;
public virtual IRedisClient Redis
{
get { return redis ?? (redis = RedisManager.GetClient()); }
}
服务执行后 gets disposed if it's created:
public virtual void Dispose()
{
...
if (redis != null)
redis.Dispose();
}
如果您不想使用从配置的 IRedisClientsManager
创建的 base.Redis
实例,那么您必须在您的服务中创建您自己的实例并自行处理它,您通常会这样做在 using 块中创建和处理,例如:
public object Get(S3Request request)
{
using (var redis = RedisUsersCredentials.CreateRedisClient())
{
// Do a bunch of stuff with Redis
}
}
每次您的服务将使用未被任何其他线程共享的新客户端实例时,通过从 IRedisClientsManager
解析和释放新的 IRedisClient
。