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

Как правильно использовать пулы соединений в redis?

Мне непонятно, как работают пулы соединений и как их правильно использовать. Я надеялся, что кто-то сможет это сделать. Я набросал свой пример использования ниже:

settings.py:

import redis

def get_redis_connection():
    return redis.StrictRedis(host='localhost', port=6379, db=0)

task1.py

import settings

connection = settings.get_redis_connection()

def do_something1():
    return connection.hgetall(...)

task2.py

import settings

connection = settings.get_redis_connection()

def do_something1():
    return connection.hgetall(...)

и др.


В основном у меня есть файл settings.py, который возвращает redis-соединения, и несколько разных файлов задач, которые получают соединения redis, а затем запускают операции. Таким образом, каждый файл задачи имеет свой собственный экземпляр redis (который предположительно является очень дорогим). Какой лучший способ оптимизации этого процесса. Можно ли использовать пулы соединений для этого примера? Есть ли более эффективный способ настройки этого шаблона?

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

Спасибо

4b9b3361

Ответ 1

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

Вы можете настроить пул соединений в методе init и сделать пул глобальным (вы можете посмотреть другие варианты, если вам неудобно глобальное).

redis_pool = None

def init():
    global pool
    print("PID %d: initializing redis pool..." % os.getpid())
    redis_pool = redis.ConnectionPool(host='10.0.0.1', port=6379, db=0)

Затем вы можете восстановить соединение из пула следующим образом:

redis_conn = redis.Redis(connection_pool=redis_pool)

Кроме того, я предполагаю, что вы используете hiredis вместе с redis-py, поскольку в некоторых случаях это должно повысить производительность. Вы также проверили количество соединений, открытых на сервере redis, с существующей настройкой, поскольку она, скорее всего, достаточно высока? Вы можете использовать сообщение INFO для получения этой информации:

redis-cli info

Проверьте раздел Клиенты, в котором вы увидите поле " connected_clients", в котором будет указано, сколько соединений вы открыли для сервера redis в этот момент.

Ответ 2

Вы должны использовать обертку на основе singleton (borg pattern), написанную поверх redis-py, которая предоставит общий пул соединений для всех ваших файлов. Всякий раз, когда вы используете объект этого класса-оболочки, он будет использовать тот же пул соединений.

REDIS_SERVER_CONF = {
    'servers' : {
      'main_server': {
        'HOST' : 'X.X.X.X',
        'PORT' : 6379 ,
        'DATABASE':0
    }
  }
}

import redis
class RedisWrapper(object):
    shared_state = {}

    def __init__(self):
        self.__dict__ = self.shared_state

    def redis_connect(self, server_key):
        redis_server_conf = settings.REDIS_SERVER_CONF['servers'][server_key]
        connection_pool = redis.ConnectionPool(host=redis_server_conf['HOST'], port=redis_server_conf['PORT'],
                                               db=redis_server_conf['DATABASE'])
        return redis.StrictRedis(connection_pool=connection_pool)

Использование:

r_server = RedisWrapper().redis_connect(server_key='main_server')
r_server.ping()

UPDATE

Если ваши файлы работают как разные процессы, вам придется использовать redis-прокси, который объединит для вас соединения, и вместо прямого подключения к redis вам придется использовать прокси-сервер. Очень устойчивым прокси-сервером redis (и memcached) является twemproxy, созданный твиттером, с основной целью которого является сокращение открытых соединений.

Ответ 3

Здесь цитата прямо из Cheese Shop страница.

За кулисами redis-py использует пул соединений для управления соединениями с сервером Redis. По умолчанию каждый созданный вами экземпляр Redis будет создавать собственный пул соединений. Вы можете переопределить это поведение и использовать существующий пул соединений, передав уже созданный экземпляр пула подключений аргументу connection_pool класса Redis. Вы можете сделать это для того, чтобы реализовать наложение клиентской стороны или иметь более точное управление зерном, как управлять соединениями.

pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool)

Кроме того, экземпляры являются потокобезопасными:

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

Вы говорите:

Таким образом, каждый файл задачи имеет свой собственный экземпляр redis (который, предположительно, очень дорог).... Для нашей системы у нас есть более десятка файлов задач, следующих за этим же шаблоном, и я заметил, что наши запросы замедляются.

Весьма маловероятно, что несколько десятков соединений могут замедлить работу сервера Redis. Но из-за того, что ваш код, за кулисами, использует пул соединений, проблема где-то вне соединений сама по себе. Redis - это хранилище в памяти, что очень быстро в большинстве возможных. Поэтому я предпочел бы искать проблему в задачах.

Update

Из комментария @user3813256. Да, он использует пул соединений на уровне задачи. Обычный способ использования встроенного пула соединений пакета redis - это просто поделиться соединением. Проще всего, ваш settings.py может выглядеть так:

import redis

connection = None

def connect_to_redis():
    global connection
    connection = redis.StrictRedis(host='localhost', port=6379, db=0)

Затем где-то при загрузке вашего приложения вызывается connect_to_redis. Затем используйте import connection в модулях задач.