第 3 单元:从 Google Cloud NDB 迁移到 Cloud Datastore

1. 概览

本系列 Codelab(自定进度的实操教程)旨在指导 Google App Engine(标准环境)开发者完成一系列迁移,从而对应用进行现代化改造。最重要的步骤是摆脱原始的运行时捆绑服务,因为下一代运行时更加灵活,为用户提供了更多的服务选项。移至新一代运行时,您可以更轻松地与 Google Cloud 产品集成,使用更多支持的服务,并支持当前的语言版本。

本可选教程向开发者介绍了如何从 Cloud NDB 迁移到 Cloud Datastore 作为客户端库,以便与 Datastore 服务通信。偏爱 NDB 的开发者可以使用 NDB,因为它与 Python 3 兼容,因此这是可选迁移。此迁移仅适用于希望与已在使用 Cloud Datastore 的其他应用构建一致的代码库和共享库的用户。详情请参阅部分。

您将了解如何

  • 使用 Cloud NDB(如果您不熟悉)
  • 从 Cloud NDB 迁移到 Cloud Datastore
  • 进一步将应用迁移到 Python 3

所需条件

  • 具备具有有效 GCP 结算账号的 Google Cloud Platform 项目
  • 基本 Python 技能
  • 基本 Linux 命令的实践知识
  • 具备开发和部署 App Engine 应用的基础知识
  • 可运行的模块 2 App Engine 2.x3.x 应用。

调查问卷

如何使用此 Codelab?

<ph type="x-smartling-placeholder"></ph> 仅仔细阅读 阅读并完成练习

2. 背景

虽然 Cloud NDB 是适用于长期 App Engine 开发者的出色 Datastore 解决方案,有助于过渡到 Python 3,但它并不是 App Engine 开发者访问 Datastore 的唯一方式。当 App Engine 的 Datastore 于 2013 年成为自己的产品时,Google Cloud Datastore 创建了一个新的客户端库,以便所有用户都能使用 Datastore。

已引导 Python 3 App Engine 和 App Engine 开发者使用 Cloud Datastore(而非 Cloud NDB)。我们鼓励 Python 2 App Engine 开发者从 ndb 迁移到 Cloud NDB,并从该处移植到 Python 3,但他们也可以选择进一步迁移到 Cloud Datastore。这一决策尤其适合已在使用 Cloud Datastore 的代码(例如刚刚提到的那些)并希望跨所有应用创建共享库的开发者。与代码一致性一样,代码重用也是一种最佳做法,并且两者都有助于降低整体的维护成本,总结如下:

从 Cloud NDB 迁移到 Cloud Datastore

  • 让开发者只需专注于单个代码库,即可访问 Datastore
  • 避免使用 Cloud NDB 维护部分代码,使用 Cloud Datastore 维护其他代码
  • 提高代码库的一致性和更好的代码可重用性
  • 支持使用通用/共享库,从而降低整体维护成本

此次迁移包括以下主要步骤:

  1. 设置/准备工作
  2. 将 Cloud NDB 替换为 Cloud Datastore 客户端库
  3. 更新应用

3. 设置/准备工作

在开始学习本教程的主要部分之前,让我们设置项目、获取代码,然后部署基准应用,以便我们知道我们从工作代码开始。

1.设置项目

如果您已完成第 2 单元 Codelab,我们建议您重复使用同一项目(和代码)。或者,您可以创建一个全新的项目或重复使用另一个现有项目。确保该项目具有有效的结算账号,并且已启用 App Engine(应用)。

2. 获取基准示例应用

前提条件之一是拥有有效的模块 2 示例应用。如果您已完成该教程,请使用您的解决方案。您可以立即完成(上面的链接),如果想要跳过,请复制第 2 单元的代码库(链接如下)。

无论您使用我们的代码还是我们的代码,我们都将从第 2 单元的代码开始。第 3 单元 Codelab 将逐步引导您完成每个步骤,完成后,它应该类似于“完成”处的代码。本教程提供 Python 2 和 Python 3 版本,因此请从下面获取正确的代码库。

Python 2

Python 2 模块 2 启动文件(您或我们的文件)的目录应如下所示:

$ ls
README.md               appengine_config.py     requirements.txt
app.yaml                main.py                 templates

如果您已完成第 2 单元的教程,还会得到一个包含 Flask 及其依赖项的 lib 文件夹。如果您没有 lib 文件夹,请使用 pip install -t lib -r requirements.txt 命令创建该文件夹,以便我们在下一步中部署此基准应用。如果您同时安装了 Python 2 和 Python 3,我们建议您使用 pip2(而不是 pip),以避免与 Python 3 混淆。

Python 3

Python 3 第 2 单元开始文件(您自己的或我们的)的目录应如下所示:

$ ls
README.md               main.py                 templates
app.yaml                requirements.txt

libappengine_config.py 均不用于 Python 3。

3. (重新)部署模块 2 应用

现在需要执行的其余准备工作步骤:

  1. 熟悉 gcloud 命令行工具(必要的话)
  2. (重新)将模块 1 代码部署到 App Engine(必要的话)

当您成功执行这些步骤并确认操作有效后,我们将在本教程中进行本教程,从配置文件开始。

4. 将 Cloud NDB 替换为 Cloud Datastore 客户端库

唯一的配置更改是 requirements.txt 文件中的次要软件包交换。

1. 更新 requirements.txt

完成模块 2 后,您的 requirements.txt 文件将如下所示:

  • 之前(Python 2 和 3):
Flask==1.1.2
google-cloud-ndb==1.7.1

通过将 Cloud NDB 库 (google-cloud-ndb) 替换为最新版本的 Cloud Datastore 库 (google-cloud-datastore) 来更新 requirements.txt,使 Flask 的条目保持不变,但请注意,与 Python 2 兼容的最终 Cloud Datastore 版本是 1.15.3:

  • 之后 (Python 2):
Flask==1.1.2
google-cloud-datastore==1.15.3
  • 之后 (Python 3):
Flask==1.1.2
google-cloud-datastore==2.1.0

请注意,代码库的维护频率比本教程更高,因此 requirements.txt 文件可能会反映更新的版本。我们建议使用每个库的最新版本,但如果这些库无法正常运行,您可以回滚到旧版本。上面的版本号是此 Codelab 上次更新时的最新版本号。

2. 其他配置文件

其他配置文件(app.yamlappengine_config.py)应与先前迁移步骤保持不变:

  • app.yaml 应(仍)引用第三方捆绑软件包 grpciosetuptools
  • appengine_config.py 应(仍)将 pkg_resourcesgoogle.appengine.ext.vendor 指向 lib 中的第三方资源。

现在,我们来看看应用文件。

5. 更新应用文件

template/index.html 没有任何变化,但 main.py 有几项更新。

1. 导入

导入部分的起始代码应如下所示:

  • 之前:
from flask import Flask, render_template, request
from google.cloud import ndb

google.cloud.ndb 导入替换为 Cloud Datastore 的导入:google.cloud.datastore。由于 Datastore 客户端库不支持在 Entity 中自动创建时间戳字段,因此还应导入标准库 datetime 模块,以手动创建一个字段。按照惯例,标准库导入高于第三方软件包导入。完成这些更改后,它应如下所示:

  • 之后:
from datetime import datetime
from flask import Flask, render_template, request
from google.cloud import datastore

2. 初始化和数据模型

初始化 Flask 后,第 2 单元的示例应用创建一个 NDB 数据模型类及其字段 lok,如下所示:

  • 之前:
app = Flask(__name__)
ds_client = ndb.Client()

class Visit(ndb.Model):
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

Cloud Datastore 库没有这样的类,因此请删除 Visit 类声明。您仍然需要一个客户端才能与 Datastore 通信,因此请将 ndb.Client() 更改为 datastore.Client()。数据存储区库更“灵活”无需“预先声明”即可创建实体,结构,例如 NDB。完成此更新后,main.py 的这一部分应如下所示:

  • 之后:
app = Flask(__name__)
ds_client = datastore.Client()

3. Datastore 访问权限

迁移到 Cloud Datastore 需要更改(在用户级)创建、存储和查询 Datastore 实体的方式。对于您的应用,此迁移的难度取决于 Datastore 代码的复杂程度。在示例应用中,我们尝试使更新尽可能简单明了。我们的起始代码如下:

  • 之前:
def store_visit(remote_addr, user_agent):
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    with ds_client.context():
        return (v.to_dict() for v in Visit.query().order(
                -Visit.timestamp).fetch_page(limit)[0])

使用 Cloud Datastore 创建一个通用实体,使用“键”识别该实体中已分组的对象。使用键值对的 JSON 对象 (Python dict) 创建数据记录,然后使用预期的 put() 将其写入 Datastore。Datastore 的查询与此类似,但更为直接。下面介绍了等效的 Datastore 代码的不同之处:

  • 之后:
def store_visit(remote_addr, user_agent):
    entity = datastore.Entity(key=ds_client.key('Visit'))
    entity.update({
        'timestamp': datetime.now(),
        'visitor': '{}: {}'.format(remote_addr, user_agent),
    })
    ds_client.put(entity)

def fetch_visits(limit):
    query = ds_client.query(kind='Visit')
    query.order = ['-timestamp']
    return query.fetch(limit=limit)

如上所述,更新 store_visit()fetch_visits() 的函数正文,使其签名与上一个版本相同。主处理程序 root() 没有任何更改。完成这些更改后,您的应用就可以使用 Cloud Datastore 了,可以进行测试了。

6. 摘要/清理

部署应用

使用 gcloud app deploy 重新部署您的应用,并确认应用能正常运行。您的代码现在应与模块 3 代码库文件夹中的内容相匹配:

如果您跳过本系列,但未执行任何上述 Codelab,则应用本身不会更改;它会记录对主网页 (/) 的所有访问,在您访问过该网站后,将如下所示:

visitme 应用

恭喜您完成此 Codelab 第 3 单元。您现在已经知道,可以同时使用 Cloud NDB Cloud Datastore 客户端库来访问 Datastore。现在,迁移到后者可为您带来诸多好处,包括共享库、通用代码和代码重复使用,以确保一致性并降低维护成本。

可选:清理

何不准备好清理,以避免在进入下一个迁移 Codelab 时继续计费?作为现有开发者,您应该已经了解了 App Engine 的价格信息

可选:停用应用

如果您尚未准备好学习下一个教程,请停用您的应用,以免产生费用。当您准备好继续学习下一个 Codelab 时,可以重新启用它。应用停用期间,不会产生任何流量费用。不过,如果您的 Datastore 用量超出免费配额,您需要另外付费,因此请删除足够多的流量,使其不超过该限额。

另一方面,如果您不继续迁移并希望彻底删除所有内容,可以关停项目

后续步骤

在这里,您可以随意浏览接下来的迁移模块:

  • 单元 3 额外知识点:继续学习奖章部分,了解如何移植到 Python 3 和下一代 App Engine 运行时。
  • 模块 7:App Engine 推送任务队列(如果您使用 [推送] 任务队列,此为必需参数)
    • 向模块 1 应用添加了 App Engine taskqueue 推送任务
    • 在第 8 单元中,帮助用户做好迁移到 Cloud Tasks 的准备
  • 第 4 单元:迁移到 Cloud Run with Docker
    • 使用 Docker 将应用容器化,以便在 Cloud Run 上运行
    • 让您可以继续使用 Python 2
  • 模块 5:使用 Cloud Buildpack 迁移到 Cloud Run
    • 使用 Cloud Buildpack 将应用容器化,以便在 Cloud Run 上运行
    • 无需了解 Docker、容器或 Dockerfile 的任何知识
    • 要求您已将应用迁移到 Python 3
  • 模块 6:迁移到 Cloud Firestore
    • 迁移到 Cloud Firestore 以使用 Firebase 功能
    • 虽然 Cloud Firestore 支持 Python 2,但此 Codelab 仅在 Python 3 中提供。

7. 额外好处:迁移到 Python 3

要使用最新的 App Engine 运行时和功能,我们建议您迁移到 Python 3。在我们的示例应用中,Datastore 是我们使用的唯一内置服务,而且自从我们已从 ndb 迁移到 Cloud NDB,我们现在就可以移植到 App Engine 的 Python 3 运行时。

概览

虽然移植到 Python 3 不在 Google Cloud 教程的范围内,但 Codelab 的这一部分可帮助开发者了解 Python 3 App Engine 运行时的不同之处。新一代运行时的一个突出功能是简化对第三方软件包的访问:无需在 app.yaml 中指定内置软件包,也无需复制或上传内置库;它们是通过在 requirements.txt 中列出的隐式安装的。

由于我们的示例非常基本,并且 Cloud Datastore 与 Python 2-3 兼容,因此应用代码无需明确移植到 3.x:该应用在 2.x 和 3.x 上运行且未经修改,这意味着在这种情况下,所需的更改仅是在配置中:

  1. 简化了 app.yaml 以引用 Python 3,并移除对捆绑的第三方库的引用。
  2. 删除 appengine_config.pylib 文件夹,因为它们不再需要。

main.pytemplates/index.html 应用文件保持不变。

更新 requirements.txt

支持 Python 2 的最终 Cloud Datastore 版本是 1.15.3。将 requirements.txt 更新为 Python 3 的最新版本(现在可能较新版本)。在编写本教程时,最新版本为 2.1.0,因此请将该行修改为以下内容(或任何最新版本):

google-cloud-datastore==2.1.0

简化 app.yaml

之前:

此示例应用的唯一实际更改是大幅缩短了 app.yaml。请注意,以下是我们在第 3 单元结束时的 app.yaml 中提供的信息:

runtime: python27
threadsafe: yes
api_version: 1

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

libraries:
- name: grpcio
  version: 1.0.0
- name: setuptools
  version: 36.6.0

之后:

在 Python 3 中,threadsafeapi_versionlibraries 指令都已废弃;所有应用都假定为线程安全应用,并且 api_version 未在 Python 3 中使用。App Engine 服务中不再预安装内置的第三方软件包,因此也废弃了 libraries。如需详细了解这些变更,请参阅有关 app.yaml 变更的文档。因此,您应从 app.yaml 中删除所有这三个版本,并更新到受支持的 Python 3 版本(见下文)。

可选:使用 handlers 指令

此外,用于将流量引导到 App Engine 应用的 handlers 指令也已被弃用。由于下一代运行时需要 Web 框架管理应用路由,因此所有“处理程序脚本”必须更改为“auto”。结合上述更改,您得到了以下 app.yaml

runtime: python38

handlers:
- url: /.*
  script: auto

如需详细了解 script: auto,请参阅 app.yaml 参考页面

移除 handlers 指令

由于 handlers 已废弃,因此您也可以移除整个部分,只留下单行 app.yaml

runtime: python38

默认情况下,这将启动 Gunicorn WSGI Web 服务器,该服务器适用于所有应用。如果您熟悉 gunicorn,以下是默认情况下使用准系统 app.yaml 启动时执行的命令:

gunicorn main:app --workers 2 -c /config/gunicorn.py

可选:使用 entrypoint 指令

不过,如果您的应用需要特定的启动命令(可以使用 entrypoint 指令指定),就会生成如下所示的 app.yaml

runtime: python38
entrypoint: python main.py

此示例明确要求使用 Flask 开发服务器,而不是 gunicorn。此外,您还必须将启动开发服务器的代码添加到应用中,以在端口 8080 上的 0.0.0.0 接口上启动,具体方法是将这小部分添加到 main.py 的底部:

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

如需详细了解 entrypoint,请参阅 app.yaml 参考页面。如需查看更多示例和最佳做法,请参阅 App Engine 标准环境启动文档App Engine 柔性环境启动文档

删除 appengine_config.pylib

删除 appengine_config.py 文件和 lib 文件夹。在迁移到 Python 3 时,App Engine 会获取并安装 requirements.txt 中列出的软件包。

appengine_config.py 配置文件用于识别第三方库/软件包,无论您是自己复制的,还是使用 App Engine 服务器(内置)上已有的库/软件包。迁移到 Python 3 后,重大变更的摘要如下:

  1. 没有捆绑复制的第三方库(列在 requirements.txt 中)
  2. 没有 pip installlib 文件夹中,即无 lib 文件夹期限
  3. 未在 app.yaml 中列出内置的第三方库
  4. 无需将应用引用到第三方库,因此不需要 appengine_config.py 文件

只需在 requirements.txt 中列出所有必需的第三方库即可。

部署应用

重新部署您的应用,确保其可以正常运行。您还可以确认您的解决方案与模块 3 示例 Python 3 代码有多接近。如需直观呈现与 Python 2 之间的差异,请将该代码与其 Python 2 版本进行比较。

恭喜您完成第 3 单元中的奖励步骤!访问关于为 Python 3 运行时准备配置文件的文档。最后,请查看上文的摘要,了解后续步骤和清理。

准备您的应用

当需要迁移应用时,您必须将 main.py 和其他应用文件移植到 3.x,因此,最佳做法是尽量编写 2.x 版应用以“向前兼容”的方式传送。

网上有很多资源可以帮助您实现这个目标,但其中一些要点如下:

  1. 确保所有应用依赖项均与 3.x 完全兼容
  2. 确保您的应用至少在 2.6 版(最好是 2.7 版)上运行
  3. 确保应用通过整个测试套件的测试(覆盖率至少达到 80%)
  4. 使用兼容性库,例如 six、Future 和/或 Modernize
  5. 了解 2.x 与 3.x 向后不兼容的关键区别
  6. 任何 I/O 都可能导致 Unicode 与字节字符串不兼容

设计示例应用时要牢记所有这些,因此为什么该应用可以直接在 2.x 和 3.x 上运行,所以我们可以集中精力向您展示使用下一代平台需要更改的内容。

8. 其他资源

App Engine 迁移模块 Codelab 问题/反馈

如果您在此 Codelab 中发现任何问题,请先搜索您的问题,然后再提交。用于搜索和创建新问题的链接:

迁移时可参考的资源

下表列出了模块 2(START)和模块 3 (FINISH) 对应的代码库文件夹的链接。您还可以从所有 App Engine 迁移的代码库访问这些迁移项目,您可以克隆或下载 ZIP 文件。

Codelab

Python 2

Python 3

模块 2

代码

代码

模块 3

代码

代码

App Engine 资源

以下是有关此特定迁移的其他资源: