Подтвердить что ты не робот

Поддержание открытого соединения Redis с помощью BookSleeve

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

Я имею в виду:

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

Такое поведение возникает, когда вы смотрите строки подключения ADO.NET. Например, строка подключения SQL Server (ConnectionString Property) имеет понятие "Объединение", "Макс. Размер пула", "Минимальный размер пула" и т.д.. Это также метод ClearAllPools, который используется для программного reset текущих пулов AppDomain, если необходимо, например.

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

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

Ответ 3

Я не программист на С#, но я бы посмотрел на проблему:

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

  • если попытка выполнить команду Redis приведет к исключению, указывающему на проблему подключения, я повторно инициализирую соединение и повторю операцию

  • Если исключение не создано, просто верните результат

Вот какой-то псевдокод:

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