使用BookSleeve维护一个打开的Redis连接

本文关键字:Redis 连接 一个 维护 BookSleeve 使用 | 更新日期: 2023-09-27 18:18:49

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

我的意思是:

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

我寻找覆盖一般Redis连接打开的代码片段,以及在每次读/写之前使用的一般'alive'检查(如果不是alive则可选唤醒)。

这个问题表明了对问题的良好态度,但它只是部分的(例如,它没有恢复丢失的连接),并且对该问题的公认答案绘制了正确的方法,但没有演示具体的代码。

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

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

重要更新(21/3/2014):

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

Marc Gravell (@MarcGravell)/StackExchange最近发布了StackExchange。最终取代Booksleeve的Redis库。这个新的库,除其他外,在内部处理重新连接,并使我的问题变得多余(也就是说,它对Booksleeve和我下面的答案都不是多余的,但我想最好的方法是开始使用新的StackExchange。复述,图书馆)。

使用BookSleeve维护一个打开的Redis连接

由于我没有得到任何好的答案,我想出了这个解决方案(顺便感谢@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;
            }
        }
    }
}

对于其他系统(如ADO.NET),这是使用连接池实现的。您从来没有真正获得一个新的Connection对象,而是实际上从池中获得一个。

池本身独立于调用者的代码管理新连接和死连接。这里的想法是有更好的性能(建立一个新的连接是昂贵的),并经受住网络问题(调用者代码将在服务器关闭时失败,但当它重新联机时恢复)。实际上,每个AppDomain、每个连接"类型"都有一个池。

当您查看ADO时,会出现这种行为。. NET连接字符串。例如SQL Server连接字符串(ConnectionString属性)有"池"、"最大池大小"、"最小池大小"等概念。这也是一个ClearAllPools方法,用于在需要时以编程方式重置当前的AppDomain池。

我没有看到BookSleeve代码中有任何类似的功能,但它似乎计划在下一个版本中发布:BookSleeve RoadMap。

同时,我想你可以写你自己的连接池,因为RedisConnection有一个错误事件,你可以用它来检测它什么时候死了。

我不是c#程序员,但我看待这个问题的方式如下:

  1. 我将编写一个通用函数,该函数将以redis连接和代表redis命令的lambda表达式为参数

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

  3. 如果没有异常则返回结果

下面是一些伪代码:

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