手动创建Redis连接可能比较艰难,不仅仅需要不断的读取配置,如果我们使用上一节的配置管理函数,我们仍然需要读取配置,连接Redis,并使用这个连接进行某种处理。为了简化所有连接,我们写了一个装饰器来打理所有Redis连接(除了保存配置的服务器)
装饰器,Python有一个语法支持将一个函数X传递给另一个函数Y。这个函数Y就叫做装饰器,装饰器提供机会去改变函数X的行为,有些装饰器验证参数,有些注册回调函数,还有一些就像我们打算做的管理连接。
我们的装饰器使用一个叫做configuration 的参数,它将生成一个包装,当调用实际函数的时候,包装函数会自动连接到合适的Redis服务器。并且这个连接将和我们之后提供的其他参数一起传递到包装函数,redis_connection()函数如下
#https://github.com/huangz1990/riacn-code/blob/master/ch05_listing_source.py#L451REDIS_CONNECTIONS = {} # 将应用组件的名字传递给装饰器。 def redis_connection(component, wait=1): #A # 因为函数每次被调用都需要获取这个配置键,所以我们干脆把它缓存起来。 key = ‘config:redis:‘ + component #B # 包装器接受一个函数作为参数,并使用另一个函数来包裹这个函数。 def wrapper(function): #C # 将被包裹函数里的一些有用的元数据复制到配置处理器。 @functools.wraps(function) #D # 创建负责管理连接信息的函数。 def call(*args, **kwargs): #E # 如果有旧配置存在,那么获取它。 old_config = CONFIGS.get(key, object()) #F # 如果有新配置存在,那么获取它。 _config = get_config( #G config_connection, ‘redis‘, component, wait) #G config = {} # 对配置进行处理并将其用于创建Redis连接。 for k, v in _config.iteritems(): #L config[k.encode(‘utf-8‘)] = v #L # 如果新旧配置并不相同,那么创建新的连接。 if config != old_config: #H REDIS_CONNECTIONS[key] = redis.Redis(**config) #H # 将Redis连接以及其他匹配的参数传递给被包裹函数,然后调用函数并返回执行结果。 return function( #I REDIS_CONNECTIONS.get(key), *args, **kwargs) #I # 返回被包裹的函数。 return call #J # 返回用于包裹Redis函数的包装器。 return wrapper
我知道这组嵌套函数容易混淆,但是这真的不是特别糟糕,函数redis_connection(),指定一个应用组件并且返回一个包装函数,这个包装函数接下来调用我们传递连接的那个函数(被包装的函数),然后返回函数调用者,这个调用者处理所有包括读取配置,连接服务器,然后调用我们的被包装函数,虽然说起来非常拗口,但实际上使用非常方便,你可以通过使用如下代码将这个装饰器应用到我们5.1.1节的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()函数时手动地向它传递日志服务器的连接了。
现在你已经知道使用redis_connection()到log_recent(),似乎并不是太糟糕,是不是?使用这个更好的方法来处理连接和配置,我们仅仅从我们调用的每个函数中移除了少数几行,做为一个练习,尝试将这个装饰器添加到5.2.3节的access_time()内容管理器从而使我们不需要传递一个连接,可以随意重用这个装饰器到这本书的所有例子。
时间: 2024-11-13 10:43:17