ServiceStack.Redis Unable to read transport - BasicRedisClientManager

跟風遠走 提交于 2019-12-24 06:27:09

问题


I am getting the following error intermittently when trying to read a redis list via ServiceStack.Redis: "Unable to read data from the transport connection: An established connection was aborted by the software in your host machine". I am wondering if my entire concept of how to reliably connect and pool connections to Redis using ServiceStack is wrong. This is my code for connecting using a sealed class and singleton pattern:

public sealed class RedisClientBase
{
    public BasicRedisClientManager Redis;
    private static readonly RedisClientBase instance = new RedisClientBase();

    private RedisClientBase()
    {
        Redis = new BasicRedisClientManager("mypassword@localhost:6379");
    }

    public static RedisClientBase Instance
    {
        get
        {
            return instance;
        }
    }
}

Then I instantiate another class that uses the singleton:

public class RedisBarSetData
{
    private static RedisClient Redis;
    protected IRedisTypedClient<BarSet> redisBarSetClient;
    protected string instrument_key;

    public RedisBarSetData()
    {
        Redis = (RedisClient)RedisClientBase.Instance.Redis.GetClient();
        redisBarSetClient = Redis.As<BarSet>();
    }

    ~RedisBarSetData()
    {
        if (Redis != null)
            Redis.Dispose();
    }

    public List<BarSet> getData(BarSets data)
    {
        setKeys(data);  // instrument_key is set in here
        var redisBarSetClientList = redisBarSetClient.Lists[instrument_key];
        List<BarSet> barSetData;

        barSetData = redisBarSetClientList.GetAll();  // <-- exception here (sometimes)
        return(barSetData);
    }
}

This is in turn instantiated and called from a "Service" DTO callback:

public class JmaSetsService : Service
{
    public object Get(JmaSets request)
    {
            RedisBarSetData barSetData = new RedisBarSetData();
            BarSets barSets = new BarSets(request);
            barSetList = barSetData.getData(barSets);
            return barSetList;
    }
}

I then use "postman" to post to this route. Most clicks on "send" come back with the data. Some end with the exception. The exception is when trying to read from redis as indicated in the code with the comment "<-- exception here". Now one other point is that I recently configured my redis to use a password by setting the config file. I mention that because I don't recall having this problem before, but that could also not be related, don't know.

In terms of freeing of the redis connection, my thinking is that my destructor calls Redis.Dispose when the RedisBarSetData() is out of scope. Is this a solid way to handle it or is there a better way. I've seen people with "using" statements when getting the pooled client, but I then have a lot of "using" statements rather than a call in just one place in the class: "Redis = (RedisClient)RedisClientBase.Instance.Redis.GetClient();" If I have a bunch of methods for the class, then I've got to repeat code in every method?

  • When I say that "I don't recall having this problem before" I am using this pattern for dozens of working DTOs. Not sure why it is failing now?

回答1:


You shouldn't hold any singleton instances of RedisClient or IRedisTypedClient<BarSet> which both encapsulates a non thread-safe Redis TCP Connection. You can instead hold singleton instances of IRedisClientsManager - which is its purpose to provide a thread-safe Redis Client Factory (like a DB Connection Pool).

If you're also using ServiceStack Services, it's easier to register dependencies in ServiceStack's IOC so IRedisClientsManager can be injected like any other dependency, e.g in AppHost.Configure():

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

This will allow you to use the base.Redis RedisClient property in your ServiceStack Services, e.g:

public class JmaSetsService : Service
{
    public object Get(JmaSets request)
    {
        var redisBarSets = base.Redis.As<BarSet>();
        return redisBarSets.Lists[instument_key].GetAll();
    }
}

If you use base.Redis you don't have to explicitly dispose of the RedisClient as it's already automatically disposed by the Service, i.e:

public class Service
{
    ...

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

You can also inject IRedisClientsManager into your own classes like any other dependency using a public property or Constructor argument, e.g:

public class RedisBarSetData
{
    public virtual IRedisClientsManager RedisManager { get; set; }

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

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

    public List<BarSet> getData(BarSets data)
    {
        setKeys(data);  // instrument_key is set in here
        return Redis.As<BarSet>().Lists[instrument_key].GetAll();
    }
}

Which you can then register and autowire in ServiceStack's IOC with:

container.RegisterAutoWired<RedisBarSetData>();

Which will then let you use it as a dependency in your Services:

public class JmaSetsService : Service
{
    public RedisBarSetData RedisBarSetData { get; set; }

    public object Get(JmaSets request)
    {
        return RedisBarSetData.getData(new BarSets(request));
    }
}

An alternative to creating your own base class is to inherit from the pre-existing LogicBase base class, which already has IRedisClientsManager property and above boilerplate.



来源:https://stackoverflow.com/questions/27738427/servicestack-redis-unable-to-read-transport-basicredisclientmanager

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