迁移到 Python 2.7

本页介绍了将 Python 应用升级到 Python 2.7 运行时所需采取的必要措施。完成本演示后,您的应用即可利用 Python 2.7 运行时的多项新功能,包括多线程Jinja2 模板引擎、字节码访问和上传以及几个新增加的第三方库

前提条件和注意事项

要使用 Python 2.7,应用必须满足以下要求:

  • 如果应用使用 Django,则必须使用 1.2 版或更高版本。如需详细了解升级,请参阅 Django 文档
  • 如果您想要使用并发请求,则应用必须使用 Web 服务器网关接口 (WSGI) 脚本处理程序(如使用 WSGI 中所述)。

除了满足这些一般前提条件外,您还必须使用某些 App Engine 功能和第三方库的特定版本。请务必更新您所拥有的版本,将其导入您的应用,并在升级后广泛测试该应用。下面列出了主要的兼容性问题,并指出了解决这些问题所需的更多资源:

  • 性能:升级到 Python 2.7 后,应用的性能可能会发生变化。如果响应延迟时间延长,您可以增加前端实例类并启用并发请求。并发请求允许您的应用在较大的实例上以较低的实例费用更快地执行。如需了解详情,请参阅实例中的请求处理
  • Django:在 Python 2.7 中,必须使用 Django 1.2 或更高版本。如需了解升级,请参阅 Django 版本说明
  • PyCryptoCrypto.Util.randpool 已弃用,取而代之的是 Crypto.Random。如需了解详情,请参阅如何处理 RandomPool
  • webapp:Python 2.7 中已弃用 webapp 模板。您可以改为直接使用 Django 模板jinja2 或您选择的其他模板引擎。
  • WebOb:Python 2.7 支持 WebOb 1.1 版。此版本与旧版本 (0.9) 不完全向后兼容。如果应用使用 WebOb,则需要对其进行广泛测试以捕获升级产生的任何错误。
  • zipimport:Python 2.7 不支持 zipimport,但 Python 2.7 可以原生方式从 .zip 文件导入
  • simplejson:Python 2.7 不支持 simplejson,但 Python 2.7 包含等效且速度更快的标准库 json 模块。

并发请求和 WSGI

最能影响应用设计和性能的 Python 2.7 特性是:它支持可处理并发请求的多线程应用。处理并发请求的能力可提高利用率,显著提高应用的性能,对于那些采用具有多个 CPU 核心的更高级实例类的应用来说,尤其如此。

为了实现多线程,应用必须从基于通用网关接口 (CGI) 的先前 Python 运行时方法转为基于网络服务器网关接口 (WSGI) 的方法。这是因为旨在串行处理请求的 CGI 脚本依赖环境变量来访问输入和输出流。

虽然用于处理请求的 WSGI 模型使应用能够更直接地访问输入和输出流(启用并发请求),但如果请求处理程序的逻辑依赖于大于本地范围的数据,或与此类数据进行交互,则同时处理多个请求会导致争用情况发生,例如应用状态。因此,为了确保新的 WSGI 应用的线程安全,采用防御性编码来处理争用情况非常重要。

如需了解详情,请参阅保证您的应用线程安全

更新 app.yaml

Python 2.7 在 app.yaml 的标头中需要一个特殊的 runtime 配置元素。请注意,Python 2.7 应用需要 threadsafe: [ true | false ] 元素。如果为 true,则 App Engine 会以并发方式发送请求;如果为 false,则 App Engine 会以串行方式发送请求。以下 app.yaml 标头会启用并发请求:

application: myapp
version: 1
runtime: python27
api_version: 1
threadsafe: true
...

使用 WSGI

使用 Python 2.7 运行时,您可以视需要直接运行 Web 服务器网关接口 (WSGI) 应用,而不必使用 run_wsgi_app 适配器将该程序作为 CGI 脚本运行。为此,请将 app.yaml 中的 CGI 处理程序(如 myapp.py)替换为 WSGI 应用程序名称(如 myapp.app)。

...
handlers:
- url: /.*
  script: myapp.app
...

您还需要将 WSGI 应用对象移动到全局范围:

import webapp2

class MainPage(webapp2.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, WebApp World!')

app = webapp2.WSGIApplication([('/', MainPage)])

""" Old code:
def main():
  run_wsgi_app(app)

if __name__ == '__main__':
  main()
"""

您仍然可以在 app.yaml 中指定 CGI 脚本处理程序;但是,由 CGI 脚本处理的请求是串行处理的,而不是并发处理的。此外,您拥有的 app.yaml 文件不能将 CGI 脚本和 WSGI 应用组合在一起,而且,如果您定义了任何 CGI 处理程序,就无法将 threadsafe 设置为 true

早期 Python 运行时的一些惯例(例如使用 main() 和检查 __name__ == 'main')现已弃用。这些措施帮助过去的 CGI 脚本保持缓存状态,但既然您直接执行 WSGI 应用,就不再需要这些步骤了。

使用应用根目录

在 Python 2.5 中,运行 CGI 脚本时,当前工作目录设置为包含该脚本的目录。这在 Python 2.7 中有所改变。利用 WSGI,请求处理程序生命周期开始时的当前工作目录为应用根目录。

检查您的应用代码,并确保编写的所有处理程序的当前工作目录为应用根目录。

配置库

Python 2.7 运行时包含一些第三方模块。其中一些库默认可用;另一些只有配置后才可用。您可以指定要使用的版本。

libraries:
- name: PIL
  version: "1.1.7"
- name: webob
  version: "1.1.1"

您可以指定应用应使用最新版模块。如果您正在开发尚无用户的应用,则此功能非常有用:您无需跟踪新版本。但是如果您的应用用户活跃度非常高,则请注意:您可能会惊讶地发现应用开始使用新的不向后兼容的库版本。如需使用最新版本,请使用以下代码:

libraries:
- name: PIL
  version: latest

如需查看受支持库的列表,请参阅第三方库

将应用设计为线程安全

如果每个处理程序仅与其范围内的变量进行交互,则处理并发请求会很简单。但是如果一个处理程序正在读取资源,与此同时另一个处理程序在修改这些资源,那么情况很快会变得复杂。即使多个请求可能正在处理同一些数据且相互干扰,也应确保您的应用按预期运行,这就是所谓的将应用设计为“线程安全”。

要将应用设计为线程安全,主要规则是尽可能频繁地限制使用共享资源(例如状态信息或全局变量)。但是,共享资源的使用通常不可能完全避免,这就是锁定对象等同步机制的用武之地。

在 Python 2.7 中,您可以访问 Python 的线程库,以便声明对逻辑块的锁定,该逻辑块会强制内部代码串行执行,而不是并发执行。请参考以下代码:

class Configuration(ndb.Model):
  some_config_data = ndb.StringProperty()
  _config_cache = None
  _config_lock = threading.Lock()
  @classmethod
  def get_config(cls):
    with cls._config_lock:
      if not cls._config_cache:
        cls._config_cache = cls.get_by_id('config')
    return cls._config_cache

此代码显示了将一些全局配置变量的缓存创建到名为 _config_cache 的变量中的过程。在这一过程中,使用名为 _config_lock 的锁定对象可确保检查预先存在的 _config_cache 行为是否可靠。否则,由于竞争请求都发现 _config_cache 为空,则该变量可能会浪费时间多次访问数据存储区,以便使用相同的数据多次设置同一个变量。

不要轻易选择使用锁定。锁定会强制执行此方法的任何其他线程进行阻止。这可能会成为性能瓶颈。

将您的应用更新到 webapp2

注意:如果不使用 webapp 作为请求处理程序,则可以跳过本部分。

Python 2.7 运行时附带的 Web 框架已从 webapp 升级为 webapp2。除此之外,webapp2 还增加了改进的 URI 路由和异常处理功能、功能全面的响应对象以及更灵活的调度机制。

webapp 模板现已弃用。您可以使用 Jinja2Django 或您选择的模板系统(只要是用纯 Python 编写的即可)来取代它们。

在 App Engine 中,webapp2 的别名为 webapp,且 webapp2 具有向后兼容性。但是,升级后您仍需要全面测试自己的应用并熟悉 webapp2新语法和功能,而不是继续依赖向后兼容性。

升级工作大功告成!

上传应用后,需要对其进行全面测试以确保向后兼容性。如果您遇到问题,请前往论坛寻求帮助。