使用BookSleeve维护打开的Redis连接

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

我的意思是:

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

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

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

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

—————————–

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

—————————–

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

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

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

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

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

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

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

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

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

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

  3. 如果没有引发exception,则返回结果

这是某种伪代码:

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