26

有没有人有通过BookSleeve库获取 Redis 的可靠模式?

我是说:

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

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

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

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

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

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

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

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

4

3 回答 3

29

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

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

池本身管理新连接和死连接,独立于调用者的代码。这里的想法是有更好的性能(建立一个新的连接是昂贵的),并在网络问题中幸存下来(调用者代码将在服务器关闭时失败,但在它恢复在线时恢复)。事实上,对于每个“类型”的连接,每个 AppDomain 都有一个池。

当您查看 ADO.NET 连接字符串时,就会出现这种行为。例如,SQL Server 连接字符串(ConnectionString 属性)具有“Pooling”、“Max Pool Size”、“Min Pool Size”等概念。这也是一种ClearAllPools方法,用于在需要时以编程方式重置当前 AppDomain 池例如。

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

同时,我想您可以编写自己的连接池,因为 RedisConnection 有一个错误事件,您可以使用它来检测它何时死亡。

于 2011-12-28T13:45:56.770 回答
2

我不是 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)
    }
}
于 2012-01-06T23:24:51.027 回答