ServiceStack.Redis multi-threading collision

 ̄綄美尐妖づ 提交于 2019-12-11 14:24:57

问题


I have a ServiceStack.Redis multi-threading collision. I have a kind of non-standard usage case for ServiceStack where I capture all routes in one "FallbackRoute". This means there is one service for that one route. Here is the DTO:

[FallbackRoute("/{Path*}")]
public class S3Request  : IRequiresRequestStream{ 
  public string Path{ get; set; }
  public Stream RequestStream { get; set; }
}

The Service is:

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
}

Then in AppHost.cs in the Configure block I have autowired redis:

container.Register<ServiceStack.Redis.IRedisClientsManager>(c =>
    new ServiceStack.Redis.BasicRedisClientManager("localhost:6379"));

    container.RegisterAutoWired<RedisUsersCredentials>();

I got this from posts on how to properly use the BasicRedisClientManager with multi-threading. But I get exceptions like:

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) &lt;IL     0x0003a, 0x00153&gt;
at ServiceStack.Redis.RedisNativeClient.ReadMultiData () &lt;IL 0x000e5,     0x0060f&gt;
at ServiceStack.Redis.RedisNativeClient.SendExpectMultiData (byte[][])     &lt;IL 0x00037, 0x001db&gt;
at ServiceStack.Redis.RedisNativeClient.SMembers (string) &lt;IL 0x0001a,     0x000cf&gt;
at    ServiceStack.Redis.Generic.RedisTypedClient`1&lt;string&gt;.GetAllItemsFromSet    (ServiceStack.Redis.Generic.IRedisSet`1&lt;string&gt;) &lt;0x00083&gt;
at ServiceStack.Redis.Generic.RedisClientSet`1&lt;string&gt;.GetAll ()     &lt;0x0006f&gt;
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) &lt;IL 0x0000c,    0x000a3&gt;
at ServiceStack.Host.ServiceRunner`1&lt;S3OL.S3Request&gt;.Execute   (ServiceStack.Web.IRequest,object,S3OL.S3Request) &lt;0x00642&gt;
</StackTrace><Errors /></ResponseStatus></ErrorResponse>

Only happens when I have more than one client. I can hit it repeatedly with one client, and very fast. If I add a client at the same time it dies in one of these Redis exceptions.


回答1:


The IRedisClient instances are not ThreadSafe and should not be shared across multiple threads. This looks like you're reusing the same RedisClient instance:

IRedisClient Redis = RedisUsersCredentials.RedisClient;

You should be accessing and releasing a new IRedisClient for each request which you can do by accessing the Redis client within the Service, e.g:

public object Put(S3Request request){
    base.Redis;
    // Do a bunch of stuff with Redis
}

Which works because it creates a new instance for that Service if it doesn't exist:

private IRedisClient redis;
public virtual IRedisClient Redis
{
    get { return redis ?? (redis = RedisManager.GetClient()); }
}

Which also gets disposed if it's created after the Service has executed:

public virtual void Dispose()
{
    ...
    if (redis != null)
        redis.Dispose();
}

If you don't want to use the base.Redis instance that gets created from the configured IRedisClientsManager than you must create your own instance within your Service and dispose of it yourself which you would typically create and dispose in a using block, e.g:

public object Get(S3Request request)
{
    using (var redis = RedisUsersCredentials.CreateRedisClient())
    {
        // Do a bunch of stuff with Redis
    }
}

By resolving and releasing a new IRedisClient from a IRedisClientsManager each time your service will be using a new client instance that's not shared by any other threads.



来源:https://stackoverflow.com/questions/29400937/servicestack-redis-multi-threading-collision

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!