web-dev-qa-db-fra.com

Comment utiliser correctement les pools de connexions dans Redis?

Je ne sais pas comment fonctionnent les pools de connexions et comment les utiliser correctement. J'espérais que quelqu'un pourrait élaborer. J'ai esquissé mon cas d'utilisation ci-dessous:

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(...)

etc.


Fondamentalement, j'ai un fichier setting.py qui renvoie les connexions redis, et plusieurs fichiers de tâches différents qui obtiennent les connexions redis, puis exécutent des opérations. Ainsi, chaque fichier de tâche a sa propre instance redis (ce qui est probablement très cher). Quelle est la meilleure façon d'optimiser ce processus. Est-il possible d'utiliser des pools de connexions pour cet exemple? Existe-t-il un moyen plus efficace de configurer ce modèle?

Pour notre système, nous avons plus d'une douzaine de fichiers de tâches suivant ce même modèle, et j'ai remarqué que nos demandes ralentissaient.

Merci

28
vgoklani

Redis-py vous fournit un pool de connexions à partir duquel vous pouvez récupérer une connexion. Les pools de connexions créent un ensemble de connexions que vous pouvez utiliser au besoin (et une fois terminé - la connexion est renvoyée au pool de connexions pour une réutilisation ultérieure). Essayer de créer des connexions à la volée sans les supprimer (c'est-à-dire ne pas utiliser un pool ou ne pas utiliser le pool correctement) vous laissera avec trop de connexions à redécouvrir (jusqu'à ce que vous atteigniez la limite de connexion).

Vous pouvez choisir de configurer le pool de connexions dans la méthode init et de rendre le pool global (vous pouvez regarder d'autres options si vous n'êtes pas à l'aise avec global).

redis_pool = None

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

Vous pouvez ensuite récupérer la connexion à partir d'un pool comme celui-ci:

redis_conn = redis.Redis(connection_pool=redis_pool)

En outre, je suppose que vous utilisez hiredis avec redis-py car cela devrait améliorer les performances dans certains cas. Avez-vous également vérifié le nombre de connexions ouvertes au serveur redis avec votre configuration existante, car il est très probablement assez élevé? Vous pouvez utiliser la commande INFO pour obtenir ces informations:

redis-cli info

Vérifiez la section Clients dans laquelle vous verrez le champ "connected_clients" qui vous indiquera le nombre de connexions ouvertes au serveur redis à cet instant.

19
ali haider

Vous devez utiliser un wrapper basé sur un singleton (modèle borg) écrit sur redis-py, qui fournira un pool de connexion commun à tous vos fichiers. Chaque fois que vous utilisez un objet de cette classe wrapper, il utilise le même pool de connexions.

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)

Usage:

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

[~ # ~] mise à jour [~ # ~]

Si vos fichiers s'exécutent en tant que processus différents, vous devrez utiliser un proxy redis qui regroupera les connexions pour vous, et au lieu de vous connecter directement à redis, vous devrez utiliser le proxy. Un proxy redis (et memcached) très stable est twemproxy créé par Twitter, avec pour objectif principal la réduction des connexions ouvertes.

9
DhruvPathak

Voici une citation directement de la fromagerie page .

En arrière-plan, redis-py utilise un pool de connexions pour gérer les connexions à un serveur Redis. Par défaut, chaque instance Redis que vous créez créera à son tour son propre pool de connexions . Vous pouvez remplacer ce comportement et utiliser un pool de connexions existant en transmettant une instance de pool de connexions déjà créée à l'argument connection_pool de la classe Redis. Vous pouvez choisir de le faire afin d'implémenter le partage côté client ou d'avoir un contrôle plus fin de la façon dont les connexions sont gérées.

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

De plus, les instances sont thread-safe :

Les instances client Redis peuvent être partagées en toute sécurité entre les threads. En interne, les instances de connexion ne sont récupérées du pool de connexions que lors de l'exécution de la commande et retournées au pool directement après. L'exécution de la commande ne modifie jamais l'état de l'instance client.

Vous dites:

Chaque fichier de tâche a donc sa propre instance redis (ce qui est probablement très cher). ... Pour notre système, nous avons plus d'une douzaine de fichiers de tâches suivant ce même modèle, et j'ai remarqué que nos demandes ralentissaient.

Il est peu probable que plusieurs dizaines de connexions puissent ralentir le serveur Redis. Mais parce que votre code, en arrière-plan, utilise un pool de connexions, le problème est quelque part hors des connexions en soi. Redis est un stockage en mémoire, donc très rapide dans la plupart des cas imaginables. Je préfère donc rechercher le problème dans les tâches.

Mise à jour

D'après le commentaire de @ user3813256. Oui, il utilise le pool de connexions au niveau de la tâche. La manière normale d'utiliser le pool de connexions intégré du package redis est simplement de partager la connexion. De la manière la plus simple, votre settings.py peut ressembler à ceci:

import redis

connection = None

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

Puis quelque part dans l'amorçage de votre appel d'application connect_to_redis. Utilisez ensuite import connection dans les modules de tâches.

6
saaj