使用BookSleeve维护打开的Redis连接

时间:2011-12-27 14:53:21

标签: c# redis booksleeve

是否有人通过BookSleeve库获取Redis的实体模式?

我的意思是:

BookSleeve的作者@MarcGravell recommends不打开&每次关闭连接,而是在整个应用程序中保持一个连接。但是你怎么能处理网络中断?即,连接可能首先成功打开,但是当某些代码尝试读取/写入Redis时,连接可能已经丢失,您必须重新打开它(如果它不能打开,则会优雅地失败 - 但是这取决于您的设计需求。)

我寻找覆盖一般Redis连接开放的代码片段,以及在每次读/写之前使用的一般“活动”检查(+可选唤醒,如果不活动)。

This question表明对这个问题有一个很好的态度,但它只是部分的(例如,它没有恢复丢失的连接),并且该问题的accepted answer以正确的方式绘制,但不是演示具体代码。

我希望这个帖子能得到可靠的答案,并最终成为一种关于BookSleeve在.Net应用程序中使用的Wiki。

-----------------------------

重要更新(2014年3月21日):

-----------------------------

Marc Gravell(@MarcGravell)/ Stack Exchange拥有recently released最终取代Booksleeve的StackExchange.Redis库。除了别的以外,这个新库在内部处理重新连接并使我的问题变得多余(也就是说,它对于Booksleeve并不是多余的,也不是我在下面的回答,但我想最好的方法是开始使用新的StackExchange.Redis库)。

3 个答案:

答案 0 :(得分:29)

由于我没有得到任何好的答案,我想出了这个解决方案(BTW感谢@Simon和@Alex的答案!)。

我想与所有社区分享,作为参考。当然,任何更正都将受到高度赞赏。

using System;
using System.Net.Sockets;
using BookSleeve;

namespace Redis
{
    public sealed class RedisConnectionGateway
    {
        private const string RedisConnectionFailed = "Redis connection failed.";
        private RedisConnection _connection;
        private static volatile RedisConnectionGateway _instance;

        private static object syncLock = new object();
        private static object syncConnectionLock = new object();

        public static RedisConnectionGateway Current
        {
            get
            {
                if (_instance == null)
                {
                    lock (syncLock)
                    {
                        if (_instance == null)
                        {
                            _instance = new RedisConnectionGateway();
                        }
                    }
                }

                return _instance;
            }
        }

        private RedisConnectionGateway()
        {
            _connection = getNewConnection();
        }

        private static RedisConnection getNewConnection()
        {
            return new RedisConnection("127.0.0.1" /* change with config value of course */, syncTimeout: 5000, ioTimeout: 5000);
        }

        public RedisConnection GetConnection()
        {
            lock (syncConnectionLock)
            {
                if (_connection == null)
                    _connection = getNewConnection();

                if (_connection.State == RedisConnectionBase.ConnectionState.Opening)
                    return _connection;

                if (_connection.State == RedisConnectionBase.ConnectionState.Closing || _connection.State == RedisConnectionBase.ConnectionState.Closed)
                {
                    try
                    {
                        _connection = getNewConnection();
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(RedisConnectionFailed, ex);
                    }
                }

                if (_connection.State == RedisConnectionBase.ConnectionState.Shiny)
                {
                    try
                    {
                        var openAsync = _connection.Open();
                        _connection.Wait(openAsync);
                    }
                    catch (SocketException ex)
                    {
                        throw new Exception(RedisConnectionFailed, ex);
                    }
                }

                return _connection;
            }
        }
    }
}

答案 1 :(得分:2)

使用其他系统(如ADO.NET),可以使用连接池实现。你永远不会真正得到一个新的Connection对象,但事实上从池中得到一个。

池本身独立于调用者的代码管理新连接和死连接。这里的想法是获得更好的性能(建立一个新的连接是可靠的),并在网络问题中存活(调用者代码将在服务器关闭时失败,但在重新联机时恢复)。实际上每个AppDomain有一个池,每个“类型”的连接。

当您查看ADO.NET连接字符串时,会发生此行为。例如,SQL Server连接字符串(ConnectionString Property)具有“池化”,“最大池大小”,“最小池大小”等概念。这也是用于编程的ClearAllPools方法例如,如果需要,请重置当前的AppDomain池。

我没有看到任何关于此类功能的内容,看看BookSleeve代码,但似乎计划在下一个版本中使用:BookSleeve RoadMap

与此同时,我认为您可以编写自己的连接池,因为RedisConnection有一个可用于此的错误事件,以检测它何时死机。

答案 2 :(得分:2)

我不是C#程序员,但我对此问题的看法如下:

  1. 我编写了一个通用函数,它将redis连接作为参数,表示Redis命令的lambda表达式

  2. 如果尝试执行Redis命令会导致指出连接问题的异常,我重新初始化连接并重试操作

  3. 如果没有引发异常,只返回结果

  4. 这是某种伪代码:

    function execute(redis_con, lambda_func) {
        try {
            return lambda_func(redis_con)
        }
        catch(connection_exception) {
            redis_con = reconnect()
            return  lambda_func(redis_con)
        }
    }