Python中使用Redis连接池的正确方法

10

如何使两个不同的模块foo.pybar.py从Redis连接池中获取连接?换句话说,我们应该如何构建应用程序?

我相信目标是为所有模块设置一个统一的连接池以获取连接。

Q1:在我的示例中,这两个模块是否都从同一个连接池获取连接?

Q2:RedisClient.py中创建RedisClient实例,然后将该实例导入其他两个模块,这样做可以吗?还是有更好的方法?

Q3:惰性加载conn实例变量是否真的有用?

RedisClient.py

import redis

class RedisClient(object):

    def __init__(self):
        self.pool = redis.ConnectionPool(host = HOST, port = PORT, password = PASSWORD)

    @property
    def conn(self):
        if not hasattr(self, '_conn'):
            self.getConnection()
        return self._conn

    def getConnection(self):
        self._conn = redis.Redis(connection_pool = self.pool)

redisClient = RedisClient()

foo.py

from RedisClient import redisClient

species = 'lion'
key = 'zoo:{0}'.format(species)
data = redisClient.conn.hmget(key, 'age', 'weight')
print(data)

bar.py

from RedisClient import redisClient

print(redisClient.conn.ping())

这个好还是那个好?

RedisClient.py

import redis

class RedisClient(object):

    def __init__(self):
        self.pool = redis.ConnectionPool(host = HOST, port = PORT, password = PASSWORD)

    def getConnection(self):
        return redis.Redis(connection_pool = self.pool)

redisClient = RedisClient()

foo.py

from RedisClient import redisClient

species = 'lion'
key = 'zoo:{0}'.format(species)
data = redisClient.getConnection().hmget(key, 'age', 'weight')
print(data)

bar.py

from RedisClient import redisClient

print(redisClient.getConnection().ping())

请问如何在Redis中正确使用连接池? - Sahil
1个回答

13

A1:是的,它们使用相同的连接池。

A2:这不是一个好的实践。因为你无法控制这个实例的初始化。一个替代方法可以使用单例模式。

import redis


class Singleton(type):
    """
    An metaclass for singleton purpose. Every singleton class should inherit from this class by 'metaclass=Singleton'.
    """
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class RedisClient(metaclass=Singleton):

    def __init__(self):
        self.pool = redis.ConnectionPool(host = HOST, port = PORT, password = PASSWORD)

    @property
    def conn(self):
        if not hasattr(self, '_conn'):
            self.getConnection()
        return self._conn

    def getConnection(self):
        self._conn = redis.Redis(connection_pool = self.pool)

RedisClient将成为一个单例类。不管您调用client = RedisClient()多少次,您都会获得相同的对象。

这样您就可以像这样使用它:

from RedisClient import RedisClient

client = RedisClient()
species = 'lion'
key = 'zoo:{0}'.format(species)
data = client.conn.hmget(key, 'age', 'weight')
print(data)

当你第一次调用 client = RedisClient() 时,实际上会初始化这个实例。

或者你可能想要根据不同的参数获取不同的实例:

class Singleton(type):
    """
    An metaclass for singleton purpose. Every singleton class should inherit from this class by 'metaclass=Singleton'.
    """
    _instances = {}

    def __call__(cls, *args, **kwargs):
        key = (args, tuple(sorted(kwargs.items())))
        if cls not in cls._instances:
            cls._instances[cls] = {}
        if key not in cls._instances[cls]:
            cls._instances[cls][key] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls][key]

在Python 3中,您使用class RedisClient(metaclass=Singleton):还是class RedisClient(BaseClass, metaclass=Singleton):?我注意到在您的示例代码中,您没有让RedisClient继承Singleton类。 - Nyxynyx
两者都是正确的。后者继承自“BaseClass”。 - Sraw
Singleton 是一个元类,你需要 metaclass=Singleton。要理解什么是元类,那是另一个大话题。 - Sraw
请问您能否详细说明当“您无法控制此实例的初始化”时可能会遇到什么问题? - Nyxynyx
1
在你的例子中,你只是创建了一个廉价实例,但如果你需要初始化一个昂贵的实例呢?当你只是import RedisClient时,你将被阻塞一段时间。更糟糕的是,你实际上需要使用RedisClient模块中的另一个类而不是redisClient。因此,在这种情况下,你从未使用过redisClient,但需要为其初始化付费。 - Sraw
谢谢,现在我明白了。 - Nyxynyx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接