Поддержание открытого соединения Redis с помощью BookSleeve - PullRequest
26 голосов
/ 27 декабря 2011

Есть ли у кого-нибудь солидный шаблон, извлекающий Redis через библиотеку BookSleeve ?

Я имею в виду:

Автор BookSleeve @MarcGravell рекомендует не открывать и не закрывать соединение каждый раз, а поддерживать одно соединение во всем приложении. Но как вы можете справиться с перебоями в сети? т.е. соединение может быть успешно открыто в первую очередь, но когда какой-то код пытается прочитать / записать в Redis, есть вероятность, что соединение было разорвано, и вы должны снова открыть его (и изящно потерпеть неудачу, если оно не открывается - но это зависит от ваших потребностей дизайна.)

Я ищу фрагменты кода, которые охватывают общее открытие соединения Redis, и общую проверку «живым» (+ необязательный пробуждение, если не живым), который будет использоваться перед каждым чтением / записью.

Этот вопрос предлагает хорошее отношение к проблеме, но оно только частичное (например, оно не восстанавливает потерянное соединение), и принятый ответ на этот вопрос привлекает внимание правильный путь, но не демонстрирует конкретный код.

Надеюсь, эта ветка получит исчерпывающие ответы и в конечном итоге станет своего рода вики в отношении использования BookSleeve в приложениях .Net.

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

ВАЖНОЕ ОБНОВЛЕНИЕ (21/3/2014):

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

Марк Грэвелл (@MarcGravell) / Stack Exchange имеет недавно выпущенную StackExchange.Redis библиотеку, которая в конечном итоге заменяет Booksleeve. Эта новая библиотека, помимо прочего, внутренне обрабатывает переподключения и делает мой вопрос излишним (то есть, это не излишне ни для Booksleeve, ни для моего ответа ниже, но я думаю, что лучший способ продвинуться вперед - это начать использовать новую библиотеку StackExchange.Redis).

Ответы [ 3 ]

29 голосов
/ 08 января 2012

Поскольку у меня нет хороших ответов, я придумала это решение (кстати, спасибо @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;
            }
        }
    }
}
2 голосов
/ 07 января 2012

Я не программист на C #, но я смотрю на проблему следующим образом:

  1. Я бы закодировал обобщенную функцию, которая бы принимала в качестве параметровСоединение redis и лямбда-выражение, представляющее команду Redis

  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)
    }
}
2 голосов
/ 28 декабря 2011

В других системах (таких как ADO.NET) это достигается с помощью пула соединений.Вы никогда не получите новый объект Connection, но на самом деле получите его из пула.

Сам пул управляет новыми и мертвыми соединениями независимо от кода вызывающего.Идея здесь в том, чтобы иметь лучшую производительность (установка нового соединения обходится дорого) и пережить проблемы с сетью (код вызывающего абонента потерпит неудачу, когда сервер не работает, но возобновит работу, когда он вернется в оперативный режим).Фактически существует один пул для каждого AppDomain для каждого «типа» соединения.

Это происходит при просмотре строк подключения ADO.NET.Например, строка подключения SQL Server ( Свойство ConnectionString ) имеет понятия «Пул», «Максимальный размер пула», «Минимальный размер пула» и т. Д. Это также метод ClearAllPools это используется для программного сброса текущих пулов доменов приложений, если это необходимо, например.

Я не вижу ничего похожего на такую ​​функцию, которая просматривает код BookSleeve, но, похоже, планируется следующий выпуск: BookSleeve RoadMap .

В то же время, я полагаю, вы можете написать свой собственный пул соединений, так как RedisConnection имеет событие ошибки, которое вы можете использовать для этого, чтобы определить, когда он мертв.

...