在GAE中实现请求级别的全局变量

标签:Google App Engine, Python

PHP里有几种不同级别的超全局变量,例如$GLOBALS、$_SERVER和$_ENV等,而GAE却不那么全面,用起来稍微有点别扭。

GAE文档里提到了handler cache和module cache,这2种缓存实际上是将main函数所在环境(包括模块的载入)缓存在1个Python实例里,使相邻较近的请求可以重用该Python实例。
该Python实例经测试至少存在30秒,一般可维持1分钟,过期后随时可能会被系统回收。
当用户发出请求时,GAE服务器会优先使用活动的Python实例去响应,若不存在活动或空闲的Python实例,且未超过单一app的实例上限(约30个),则GAE服务器会创建一个新的Python实例去响应。

因此,重用Python实例时,它的全局变量和模块内的变量是可以跨请求重用的;而各个Python实例间,则不能共享相同的环境及其全局变量。

若要实现一个请求内的全局变量,最简单的办法是在全局环境里创建一个变量,在main()函数开始时赋值,响应结束时清空。
这种方法的效率很高,但必须在主handler里提前创建所需的变量(也可以是一个字典对象),且在响应结束时可能因抛出异常而忘记清空,导致下一次响应可能会使用错误的值。
最重要的缺点是跨模块时,获取__main__里的全局变量并不方便。我的方法是把全局变量放在一个module里,然后需要用到它的就去import。

另一种方法是使用os.environ,这个对象在每次响应时由服务器进行设置,所以不会出现跨响应的情况。它的使用类似一个字典对象,但key和value都必须是字符串(且key一般是大写)。
下面这个简单的脚本就演示了如何统计响应所用时间:
from os import environ
from time import time
from google.appengine.ext import webapp
from wsgiref.handlers import CGIHandler


class MainPage(webapp.RequestHandler):
  def get(self):
    self.response.out.write(repr(time() - float(environ['TIME'])))
    # 对于webapp来说,由于它重用了webob的request对象,所以写成self.request.environ['TIME']也是可以的
    # 而web.py则可以用web.ctx.env.get('TIME')

application = webapp.WSGIApplication([('/', MainPage)])

def main():
  environ['TIME'] = repr(time())
  CGIHandler().run(application)

if __name__ == "__main__":
  main()
注意这里不适合用clock(),在GAE服务器上,它的精度只有约0.01秒,无法进行毫秒级的统计,所以我用了精度更高的time()。不过clock()可以得出该Python实例共用了多少CPU时间(跨请求的)。
此外,系统的启动时间及输出的时间并未计算在内,所以并不能精确地提供响应时间。
另外,google.appengine.api.quota模块则可用于计算CPU和API CPU时间。

使用第2种方法,就可以方便地记录一次响应中,数据库、memcache的使用次数及其开销了,这也是我研究它的初衷。当然最好是自己再稍微封装一下这个实现,实现自动计时。

6条评论 你不来一发么↓ 顺序排列 倒序排列

    向下滚动可载入更多评论,或者点这里禁止自动加载

    想说点什么呢?