现在我们已经有了存储在Redis的统计数据,接下来怎么办?具体的来说,我们现在已经有了关于没个页面的访问时间(举例来说)信息,那么我们要怎样发现哪些页面需要很长时间来生成?或者我们怎么知道那些明显了比上一次生成花费了更长的时间?这个简单的答案是,当以上场景发生时,我们需要使用某种方式存储更多的信息来让我们发现问题,这就是我们这节要解释的内容。
如果我们想要记录访问时间,我们需要计算访问时间。我们将花时间在各个地方添加访问时间的计算,然后添加代码来保存访问时间,或者实现某些东西来帮助我们计算和存储访问时间。那么同样助手也可以使得那些信息可用,(比如)存储在ZSET里的平均访问最慢的页面信息,甚至可用取得那些比以前生成页面花费更多时间的报告。
为了帮助我们计算和存储访问时间,我们需要写一个Python context manager来包装我们计算和存储访问时间的代码,这个context manager将获取当前时间,执行包装代码,然后计算总执行时间,记录到Redis,然后更新一个最高访问时间的ZSET。下面这个代码清单展示了我们的context manager如何执行这组操作。
#代码来源https://github.com/huangz1990/riacn-code/blob/master/ch05_listing_source.py#L283@contextlib.contextmanager def access_time(conn, context): # 记录代码块执行前的时间。 start = time.time() # 运行被包裹的代码块。 yield # 计算代码块的执行时长。 delta = time.time() - start # 更新这一上下文的统计数据。 stats = update_stats(conn, context, ‘AccessTime‘, delta) # 计算页面的平均访问时长。 average = stats[1] / stats[0] pipe = conn.pipeline(True) # 将页面的平均访问时长添加到记录最慢访问时间的有序集合里面。 pipe.zadd(‘slowest:AccessTime‘, context, average) # AccessTime有序集合只会保留最慢的100条记录。 pipe.zremrangebyrank(‘slowest:AccessTime‘, 0, -101) pipe.execute()
在access_time() context manager 中有些神奇的事情,他有助于我们明白在使用过程中发生了什么。下面的代码展示了access_time() context manager 被用来通过一种类似于第二章使用的中间件或者插件来记录网页访问时间:
def process_view(conn, callback): # 计算并记录访问时长的上下文管理器就是这样包围代码块的。 with access_time(conn, request.path): # 当上下文管理器中的yield语句被执行时,这个语句就会被执行。 return callback()
看完这个例子之后,即使你不知道如何创建一个context manager,你至少知道怎么去使用它。在这个例子中我们使用了一个access_time context manager 来计算生成一个页面的总时间。这个context manager也可以用来记录数据库查询时间或者渲染一个模板的时间。作为练习,你能思考另外一种有用的context manager来记录统计信息?或者你能够添加超出两个标准偏差的访问时间报告到recent_log()?
context manager