在GAE上使用Python 2.7的注意事项

标签: gae python 注意 | 发表时间:2011-10-16 14:24 | 作者:keakon ndv
出处:http://www.keakon.net/
随着GAE SDK 1.5.5版的发布,开发者终于可以使用Python 2.7了。
不过今天我试用了一下,发现了一些需要注意的问题,于是记录在此。

  1. 它目前还是个实验性质的runtime,因此还没法本地测试,必须部署到云端。
  2. 它只支持HR datastore,不符合条件的需要迁移数据。
  3. 一些库的版本变了,在app.yaml的libraries部分可以配置。
  4. 增加了一些C库,可以加快性能。当不确定runtime版本时,可以使用这种方式来引入:
    try:
    	import json
    except ImportError:
    	import simplejson as json
    或者判断runtime版本:
    if os.environ['APPENGINE_RUNTIME'] == 'python27':
    	import json
    else:
    	import simplejson as json
  5. 它支持CGI和WSGI这2种handler。
    CGI其实也有2种方式:
    1. 解析os.environ,用print或sys.out.write输出响应(包括HTML头)。
    2. 生成一个WSGI应用,在main()函数里传递给google.appengine.ext.webapp.util.run_wsgi_app()来执行。
    使用CGI方式时,app.yaml的script写这个脚本的路径,例如main.py。
    这种方式会缓存脚本的main()函数,在处理后续的请求时,将直接执行main()函数。

    WSGI方式和CGI的第2种方式很像,只不过生成WSGI应用后,不需要自己运行。并且__name__ == '__main__'也是不成立的,main()函数也不会被缓存。
    而在app.yaml的script里写的是这个应用的路径,例如main.application(必须为模块的全局变量)。
    一些内置的handler可以用builtins来开启,另一些则需要修改,例如“$PYTHON_LIB/google/appengine/ext/admin”要改成“google.appengine.ext.admin.application”。这些应用名一般都是用application,具体情况可以查看SDK源码。

    实际上WSGI方式使用的是/base/python27_runtime/python27_lib/versions/1/google/appengine/runtime/wsgi.py这个脚本,可惜SDK中并没有包含它,只能用dir(google.appengine.runtime.wsgi)之类的方式来窥视一下。
  6. 它支持并发请求,这种情况下必须在app.yaml里设置threadsafe: true,并使用WSGI方式。
    这里的线程安全实际上就是指不要滥用全局变量(可以使用常量)。
    在非并发方式下,处理一个请求的过程中,全局变量只会被当前线程改变,因此很容易控制。而在并发方式下,全局变量随时可能被其他线程改变,因此就变得不可靠了。

    以Doodle的hook机制为例,之前我是这样做的:
    hook.py:
    request_arrive_time = 0
    db_count = 0
    db_time = 0
    db_start_time = 0
    
    def before_db(service, call, request, response):
    	global db_count, db_start_time
    	db_count += 1
    	db_start_time = time()
    
    def after_db(service, call, request, response):
    	global db_time
    	dt = time() - db_start_time
    	db_time += dt
    
    apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('before_db', before_db, 'datastore_v3')
    apiproxy_stub_map.apiproxy.GetPostCallHooks().Push('after_db', after_db, 'datastore_v3')
    blog.py:
    def main():
    	hook.db_count = 0
    	hook.db_time = 0
    	hook.db_start_time = 0
    	hook.request_arrive_time = time()
    	util.run_wsgi_app(application)
    
    if __name__ == '__main__':
    	main()
    我在hook.py中使用了4个全局变量,如果在并发方式下,这种实现方式就可能会记录下错误的数据。
    另一个要注意的是,在WSGI方式下,main()函数并不会被运行。

    为了解决这2个问题,我进行了如下修改:
    class WsgiApplication(yui.WsgiApplication):
    	def __call__(self, environ, start_response):
    		local.request_arrive_time = time()
    		local.db_count = 0
    		local.db_time = 0
    		local.db_start_time = 0
    		return super(WsgiApplication, self).__call__(environ, start_response)
    
    
    local = threading.local()
    local.request_arrive_time = 0
    local.db_count = 0
    local.db_time = 0
    local.db_start_time = 0
    
    def before_db(service, call, request, response):
    	if hasattr(local, 'db_count'):
    		local.db_count += 1
    	else:
    		local.db_count = 1
    	local.db_start_time = time()
    
    def after_db(service, call, request, response):
    	if hasattr(local, 'db_start_time') and hasattr(local, 'db_time'):
    		dt = time() - local.db_start_time
    		local.db_time += dt
    先说threading.local(),它会生成一个线程安全的local对象。
    假设线程1将这个对象的db_count属性设为1,线程2将其设为2,之后在获取时,它们分别会获取到1和2,而不会被其他线程覆盖。
    如此一来,它就变成了线程安全的全局变量了。

    再说那个WsgiApplication,它是一个WSGI应用类。
    这个类的对象就是一个WSGI应用,在app.yaml中将其设为script后,处理新请求的入口就是它的__call__()方法。
    因此,我把需要在main()里做的事移到它的开始部分,即可完成初始化的功能。

    此外,我还能将其当成一个简单的函数来处理:
    def hook_app(app):
    	def wrap(environ, start_response):
    		local.request_arrive_time = time()
    		local.db_count = 0
    		local.db_time = 0
    		local.db_start_time = 0
    		return app(environ, start_response)
    	return wrap
    而在blog.py中还需要手动封装一下:
    application = hook.hook_app(application)

    最后,有些handler可能没有import hook(例如SDK自带的),但也访问了数据库,这种情况下before_db()和after_db()仍会执行,但local的4个属性都是没有设置的。
    因此,为了避免出错,还需要用hasattr()来检测这些属性是否存在。

相关 [gae python 注意] 推荐:

在GAE上使用Python 2.7的注意事项

- ndv - keakon的涂鸦馆
随着GAE SDK 1.5.5版的发布,开发者终于可以使用Python 2.7了. 不过今天我试用了一下,发现了一些需要注意的问题,于是记录在此. 它目前还是个实验性质的runtime,因此还没法本地测试,必须部署到云端. 它只支持HR datastore,不符合条件的需要迁移数据. 一些库的版本变了,在app.yaml的libraries部分可以配置.

注意 Python 中 strptime 的效率问题

- CMGS - python.cn(jobs, news)
Python中datetime,time等类型都有strptime方法,将时间字符串根据格式解析成相应的对象. 很多时候我们的需求只是解析”%Y-%m-%d %H:%M:%S”格式的字符串,而strptime会根据locale作相应不同的处理,增加了不必要的复杂度,在某些场合成为了性能瓶颈. 在python的mail-list上早有人提出这个问题,里面提到使用正则表达式解析.

GAE SDK 1.5.5 版发布

- Ken - python.cn(jobs, news)
本想睡觉了,突然看到GAE SDK 1.5.5版发布了,于是就再坚持一下,写完本文吧. 这个版本最重要的更新就是支持Python 2.7了. 关于Python 2.7的新功能,可以查看《What's New in Python 2.7》这篇文档. 在app.yaml中设置threadsafe: true即可启用,必须使用WSGI接口(直接在app.yaml里设置WSGI application对象的路径,而非Python文件).

GAE SDK 1.5.5版发布

- f41c0n - keakon的涂鸦馆
本想睡觉了,突然看到GAE SDK 1.5.5版发布了,于是就再坚持一下,写完本文吧. 这个版本最重要的更新就是支持Python 2.7了. 关于Python 2.7的新功能,可以查看《What's New in Python 2.7》这篇文档. 在app.yaml中设置threadsafe: true即可启用,必须使用WSGI接口(直接在app.yaml里设置WSGI application对象的路径,而非Python文件).

GAE SDK 1.6.2版发布

- - keakon的涂鸦馆
好久没发布新文章了,出来冒个泡,顺便祝大家新春快乐. 也许有人已经知道了,我辞职后就一直在家玩游戏,所以没学什么新玩意来分享,也没空去维护那几个Chrome扩展了. 实在是冬天太冷,坐在我家的沙发上也不好码代码,所以还是等春暖花开之时,再重操旧业吧. 废话就说到这了,赶紧介绍下今天刚发布的 GAE SDK 1.6.2版:.

GAE SDK 1.6.1版发布

- - keakon的涂鸦馆
早上醒来后,发现 今年最后一个GAE SDK版本发布了. 不过因为赖床,就拖到现在才更新博客了. 顺便还发现Mac OS X上的GoogleAppEngineLauncher和Chrome一样可以自动更新了. 几秒钟就更新完毕了,不知道是不是直接比较差异来更新的. 而在查看源码时,我还发现ndb和GAE文档也加入SDK中了.

GAE、SAE、BAE 对比分析

- - CSDN博客云计算推荐文章
目前,云服务很多,例如GAE、BAE、SAE、TAE、CAE、ACE、EC2、AZURE各种云. 本文主要从以下几个方面对GAE、SAE和BAE的优劣进行分析. 阿里云:  http://www.aliyun.com/. GAE 目前使用 Datasotre 存取数据,最近也提供了云 SQL(MySQL),但申请比较困难.

GoAgent又一个基于GAE的穿越利器

- ayu - iGFW
GoAgent是 一个使用Python和Google Appengine SDK编写的代理软件. 部署和使用方法非常简单,不需要安装Python或者Google Appenginge SDK ,几分钟即可搞定. GoAgent『申请与创建』. 首先申请 注册一个Google App Engine账号(点此注册).

GOAGENT又一个基于GAE的穿越利器

- zhai - PT Ubuntu Blog
GoAgent是 一个使用Python和Google Appengine SDK编写的代理软件. 部署和使用方法非常简单,不需要安装Python或者Google Appenginge SDK ,几分钟即可搞定. GoAgent『申请与创建』. 首先申请 注册一个Google App Engine账号(点此注册).

GAE 博客——B3log Solo 0.3.0 正式版发布了!

- king - 开源中国社区最新新闻
GAE 博客 —— B3LOG Solo 0.3.0 正式版发布了. 该版本重写了后台管理,对前台模版进行了调整优化,修复了一些 Bugs,并进行了一些改进以及新特性.