在GAE中使用hook
2009 11 10 09:39 PM 1457次查看
分类:Google App Engine 标签:Google App Engine, Python
而最近在考虑做blog,想像Discuz!一样做个性能评估,将页面生成时间、数据库访问次数等输出出来。
要实现这个功能的话,一般是需要自己封装一下数据库调用函数,在每次调用时记录下时间和增加调用次数,不过这样改动太大,所以就想到了hook,于是再次读了一遍。
原文的例子有点难懂,所以这里我就先给个简单的例子,实际上hook是非常容易使用的:
from google.appengine.api import apiproxy_stub_map
from google.appengine.ext import db
class Test(db.Model):
pass
def hook(service, call, request, response):
print 'Hello, datastore!'
apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('db_hook', hook, 'datastore_v3')
Test().put()
在这个例子里面,我定义了一个Test模型,它只是用来访问数据库的一个工具而已。接着定义了一个hook函数,这个函数一旦被注册,就会在服务被调用时自动调用。这里我只是输出了一句话而已。
接着就是关键代码了,GetPreCallHooks()是表示在服务调用前来调用我的hook,Append()是将hook放在调用队列的最后面。
这个函数接收3个参数,分别为key、function和service。Key是标记一个hook用的字符串,如果调用队列中已有该key,则再次添加的时候会忽略掉;function就是需要调用的函数,它的参数我用不到,你可以去原文看看,和所调用的服务有关;service则是被hook的服务名了,数据库操作是'datastore_v3'。至于服务名的文档我没找到,不过可以在GAE SDK的源代码里搜索apiproxy_stub_map.MakeSyncCall,就会发现'user'、'xmpp'、'images'之类的服务名了。此外,虽然没找到memcache,但它的服务名就是'memcache'。
最后,我创建了一个Test实体,然后保存到数据库,这时hook函数就会被调用,输出'Hello, datastore!'。
看懂这个例子后,要实现我所要达到的目标就不难了,于是我就直接上代码吧:
from google.appengine.api import apiproxy_stub_map
from google.appengine.ext import db
from time import time
class Test(db.Model):
pass
db_times = 0
db_time = 0
db_start_time = 0
def before_db(service, call, request, response):
global db_times, db_start_time
db_times += 1
db_start_time = time()
def after_db(service, call, request, response):
global db_time, db_start_time
db_time += time() - db_start_time
apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('before_db', before_db, 'datastore_v3')
apiproxy_stub_map.apiproxy.GetPostCallHooks().Push('after_db', after_db, 'datastore_v3')
print db_times, db_time
Test().put()
print db_times, db_time
Test().put()
print db_times, db_time
大部分代码是不需要解释了,只说说GetPostCallHooks().Push():它实际上和GetPreCallHooks().Append()相反,是在服务调用结束后才调用该hook;由于我只计算数据库时间,不想被其他hook干扰,所以就用Push()放到了队列的开头,保证它是第一个被调用的hook。最终这个程序的运行结果如下:
0 0成功达到了我的目的,顺便还能了解到,保存一个空的实体需要约0.03秒的数据库时间,还算过得去。
1 0.0285320281982
2 0.058926820755
至于如何跨模块地使用这些性能评估用的全局变量,可以参考《在GAE中实现请求级别的全局变量》。
当然,最方便的方法是将hook函数放在一个模块里,将性能评估用的变量作为该模块的全局变量,然后在__main__里导入并注册这些hook即可。
向下滚动可载入更多评论,或者点这里禁止自动加载。