dot Redis 8 来了——而且是开源的

了解更多

5.4.3 自动 Redis 连接管理

返回主页

5.4.3 自动 Redis 连接管理

手动创建连接并将其传递给 Redis 可能很困难。我们不仅需要反复参考配置信息,而且如果我们使用上一节中的配置管理功能,我们仍然需要获取配置、连接到 Redis,并以某种方式处理连接。为了简化所有这些连接的管理,我们将编写一个装饰器,它将负责连接到我们所有的 Redis 服务器(除了配置服务器)。

装饰器在 Python 中,有一种语法可以将函数 X 传递到另一个函数 Y 中。这个函数 Y 称为装饰器。装饰器有机会改变函数 X 的行为。一些装饰器验证参数,其他装饰器注册回调,甚至其他装饰器管理连接,就像我们打算的那样。

我们的装饰器将以命名配置作为参数,这将生成一个包装器,当在实际函数上调用时,将包装该函数,以便以后的调用将自动连接到适当的 Redis 服务器,并且该连接将传递给包装的函数,以及所有其他后来提供的参数。下一个列表包含我们的 redis_connection() 函数的源代码。

列表 5.16 redis_connection() 函数/装饰器
REDIS_CONNECTIONS = {}

 

 

def redis_connection(component, wait=1):

我们将应用程序组件的名称传递给装饰器。

 

 

   key = 'config:redis:' + component

我们缓存配置键,因为每次调用该函数时都会获取它。

 

 

   def wrapper(function):

我们的包装器接受一个函数,它用另一个函数包装该函数。

 

 

      @functools.wraps(function)

将一些有用的元数据从原始函数复制到配置处理程序。

 

 

      def call(*args, **kwargs):

创建实际的函数,它将管理连接信息。

 

 

         old_config = CONFIGS.get(key, object())       

获取旧的配置(如果有)。

 

 

         _config = get_config(
            config_connection, 'redis', component, wait)

获取新的配置(如果有)。

 

 

         config = {}

 

 

         for k, v in _config.iteritems():
            config[k.encode('utf-8')] = v

使配置可用于创建 Redis 连接。

 

 

         if config != old_config:
            REDIS_CONNECTIONS[key] = redis.Redis(**config)

如果新旧配置不匹配,则创建一个新连接。

 

 

         return function(
            REDIS_CONNECTIONS.get(key), *args, **kwargs)

调用并返回包装函数的结果,记住传递连接和其他匹配的参数。

 

 

   return call

返回完全包装的函数。

 

 

return wrapper

返回一个可以包装我们的 Redis 函数的函数。

 

 

 

组合 *args**kwargs早在第 1 章中,我们首先了解了 Python 中的默认参数。但是在这里,我们结合了两种不同的参数传递形式。如果您难以理解正在发生的事情(这本质上是在函数定义中捕获 argskwargs 变量中的所有位置和命名参数,并将所有位置和命名参数传递给被调用的函数),那么您应该花一些时间通过此缩短的 URL 阅读 Python 语言教程:http://mng.bz/KM5x

我知道这组嵌套函数一开始可能会令人困惑,但实际上并没有那么糟糕。我们有一个函数 redis_connection(),它接受命名的应用程序组件并返回一个包装器函数。然后使用我们想要传递连接的函数(包装的函数)调用该包装器函数,然后返回函数调用者。此调用者处理获取配置信息、连接到 Redis 和调用我们的包装函数的所有工作。虽然描述起来很麻烦,但实际使用起来很方便,您可以通过在下一个列表中将其应用于第 5.1.1 节中的 log_recent() 函数来看到这一点。

列表 5.17 装饰后的 log_recent() 函数
@redis_connection('logs')

redis_connection() 装饰器非常易于使用。

 

 

def log_recent(conn, app, message):

函数定义没有改变。

 

 

   'the old log_recent() code'

 

 

log_recent('main', 'User 235 logged in')

我们不再需要在调用 log_recent() 时担心传递日志服务器连接。

 

 

 

装饰器除了列表 5.16 中使用 *args 和 **kwargs 进行的奇怪的参数传递之外,我们还使用语法来“装饰”日志函数。也就是说,我们将一个函数传递给一个装饰器,该装饰器对该函数执行一些操作,然后返回原始函数或其他内容。您可以在 https://pythonlang.cn/dev/peps/pep-0318/ 上阅读有关正在发生的事情以及原因的详细信息。

既然您已经了解了如何在 log_recent() 上使用 redis_connection() 装饰器,它看起来是不是没那么糟糕了?通过这种更好的连接和配置处理方法,我们刚刚从几乎所有要调用的函数中删除了几行代码。作为练习,尝试将此装饰器添加到第 5.2.3 节中的 access_time() 上下文管理器中,这样我们就不需要传递连接了。您可以随意在本书中的所有其他示例中重复使用此装饰器。