1. 概览
无服务器迁移站系列 Codelab(自定进度的动手教程)和相关视频旨在帮助 Google Cloud 无服务器开发者通过一次或多次迁移(主要是从旧版服务迁移)来指导他们的应用现代化。这样做可让您的应用更易于移植,并为您提供更多选择和灵活性,使您能够与更多 Cloud 产品集成并访问这些产品,还能更轻松地升级到新的语言版本。虽然最初侧重于最早的 Cloud 用户(主要是 App Engine [标准环境] 开发者),但本系列文章的范围足够广,可涵盖其他无服务器平台(例如 Cloud Functions 和 Cloud Run),或者其他平台(如果适用)。
此 Codelab 的目的是向 Python 2 App Engine 开发者展示如何从 App Engine Memcache 迁移到 Cloud Memorystore(适用于 Redis)。此外,还可以从 App Engine ndb隐式迁移到 Cloud NDB,但此迁移主要在模块 2 Codelab 中介绍;请查看该 Codelab 以获取更多分步信息。
在接下来的实验中
- 设置 Cloud Memorystore 实例(通过 Cloud 控制台或
gcloud工具) - 设置 Cloud 无服务器 VPC 访问通道连接器(通过 Cloud 控制台或
gcloud工具) - 从 App Engine Memcache 迁移到 Cloud Memorystore
- 在示例应用中使用 Cloud Memorystore 实现缓存
- 从 App Engine
ndb迁移到 Cloud NDB
所需条件
- 具有有效结算账号的 Google Cloud 项目(此为非免费 Codelab)
- 基本 Python 技能
- 常用 Linux 命令的实践知识
- 具备开发和部署 App Engine 应用的基础知识
- 有效的模块 12 App Engine 应用(完成模块 12 Codelab [推荐] 或从代码库复制模块 12 应用)
调查问卷
您打算如何使用本教程?
您如何评价使用 Python 的体验?
您如何评价自己在使用 Google Cloud 服务方面的经验水平?
2. 背景
此 Codelab 演示了如何将示例应用从 App Engine Memcache(和 NDB)迁移到 Cloud Memorystore(和 Cloud NDB)。此过程涉及替换对 App Engine 捆绑服务的依赖项,从而提高应用的可移植性。您可以选择继续使用 App Engine,也可以考虑迁移到上文所述的任何替代方案。
与本系列中的其他迁移相比,此迁移需要付出更多精力。建议将 App Engine Memcache 替换为 Cloud Memorystore,它是一种全托管式云端缓存服务。Memorystore 支持两种常用的开源缓存引擎:Redis 和 Memcached。此迁移模块使用 Cloud Memorystore for Redis。如需了解详情,请参阅 Memorystore 和 Redis 概览。
由于 Memorystore 需要运行的服务器,因此还需要 Cloud VPC。具体而言,您必须创建无服务器 VPC 访问通道连接器,以便 App Engine 应用可以通过 Memorystore 实例的专用 IP 地址连接到该实例。完成本练习后,您将更新应用,使其在行为方式与之前保持一致的同时,将 Cloud Memorystore 作为缓存服务,取代 App Engine 的 Memcache 服务。
本教程首先介绍 Python 2 中的模块 12 示例应用,然后介绍可选的 Python 3 次要升级。如果您已熟悉如何通过 Python 3 App Engine SDK 从 Python 3 访问 App Engine 捆绑服务,则可以先学习模块 12 示例应用的 Python 3 版本。这样做需要移除对 SDK 的使用,因为 Memorystore 不是 App Engine 捆绑服务。本教程不涉及如何使用 Python 3 App Engine SDK。
本教程包含以下关键步骤:
- 设置/准备工作
- 设置缓存服务
- 更新配置文件
- 更新主应用
3. 设置/准备工作
准备 Cloud 项目
我们建议您重复使用与完成模块 12 Codelab 相同的项目。或者,您可以创建一个全新的项目或重复使用另一个现有项目。本系列中的每个 Codelab 都有一个“START”(开始时的基准代码)和一个“FINISH”(迁移后的应用)。我们提供了 FINISH 代码,以便您在遇到问题时将自己的解决方案与我们的解决方案进行比较。如果出现问题,您可以随时回滚到 START 重新开始。这些检查点旨在确保您成功学习如何执行迁移。
无论您使用哪个云项目,都请确保该项目具有有效的结算账号。另请确保 App Engine 已启用。请查看并确保您了解完成这些教程的一般费用影响。不过,与本系列中的其他 Codelab 不同,本 Codelab 使用的 Cloud 资源没有免费层级,因此完成练习会产生一些费用。系统会提供更具体的费用信息,以及有关减少用量的建议,包括最后有关释放资源以最大限度减少结算费用的说明。
获取基准示例应用
此 Codelab 从模块 12 的基准代码开始,逐步引导您完成迁移。完成本 Codelab 后,您将获得一个可正常运行的模块 13 应用,该应用与某个 FINISH 文件夹中的代码非常相似。以下是这些资源:
- START:模块 12 Python 2 (
mod12) 或 Python 3 (mod12b) 应用 - 完成:模块 13 Python 2 (
mod13a) 或 Python 3 (mod13b) 应用 - 整个迁移代码库(克隆或下载 ZIP)
START 文件夹应包含以下文件:
$ ls README.md app.yaml main.py requirements.txt templates
如果您从 Python 2 版本开始,那么还会有一个 appengine_config.py 文件,如果您完成了模块 12 Codelab,可能还会有一个 lib 文件夹。
(重新)部署模块 12 应用
您剩余的准备工作步骤:
- 熟悉
gcloud命令行工具(如有必要) - (重新)将模块 12 代码部署到 App Engine(如有必要)
Python 2 用户应使用以下命令删除并重新安装 lib 文件夹:
rm -rf ./lib; pip install -t lib -r requirements.txt
现在,所有用户(Python 2 和 3 用户)都应使用以下命令将代码上传到 App Engine:
gcloud app deploy
成功部署后,请确认该应用的外观和功能与模块 12 中的应用(一种可跟踪访问次数的 Web 应用,可将访问次数缓存一小时以供同一用户使用)完全相同:

由于最近的访问记录已缓存,因此网页刷新应能以相当快的速度加载。
4. 设置缓存服务
Cloud Memorystore 不是无服务器的。需要一个实例;在本例中,需要一个正在运行的 Redis 实例。与 Memcache 不同,Memorystore 是一款独立的 Cloud 产品,没有免费层级,因此请务必先查看 Memorystore for Redis 价格信息,然后再继续操作。为了最大限度地降低此练习的费用,我们建议使用最少的资源来运行:基本服务层级和 1 GB 容量。
Memorystore 实例与 App Engine 应用(实例)位于不同的网络中,因此必须创建无服务器 VPC 访问通道连接器,以便 App Engine 能够访问您的 Memorystore 资源。为尽可能降低 VPC 费用,请选择实例类型 (f1-micro) 和最少的请求实例数(建议最少 2 个,最多 3 个)。另请参阅 VPC 价格信息页面。
在引导您创建每项必需资源的过程中,我们会重复这些降低费用的建议。此外,在 Cloud 控制台中创建 Memorystore 和 VPC 资源时,您会在右上角看到每种产品的价格计算器,其中会显示每月费用估算值(请参见下图)。如果您更改选项,这些值会自动调整。您应该会看到类似如下内容:

这两种资源都是必需的,先创建哪种资源并不重要。如果您先创建 Memorystore 实例,则 App Engine 应用在没有 VPC 连接器的情况下无法访问该实例。同样,如果您先创建 VPC 连接器,则该 VPC 网络上没有任何内容可供您的 App Engine 应用与之通信。本教程将引导您先创建 Memorystore 实例,然后再创建 VPC 连接器。
当这两个资源都处于在线状态时,您需要将相关信息添加到 app.yaml,以便应用可以访问缓存。您还可以参考官方文档中的 Python 2 或 Python 3 指南。您还可以参考 Cloud NDB 迁移页面(Python 2 或 Python 3)上的数据缓存指南。
创建 Cloud Memorystore 实例
由于 Cloud Memorystore 没有免费层级,因此我们建议您分配最少的资源来完成此 Codelab。您可以使用以下设置将费用降至最低:
- 选择最低服务层级:基本(控制台默认:“标准”,
gcloud默认:“基本”)。 - 选择最小存储空间:1 GB(控制台默认值:16 GB,
gcloud默认值:1 GB)。 - 通常,任何软件的最新版本都需要最多的资源,但选择最旧的版本可能也不建议。目前第二最新的版本是 Redis 版本 5.0(控制台默认版本:6.x)
了解这些设置后,下一部分将引导您通过 Cloud 控制台创建实例。如果您希望通过命令行执行此操作,请跳过此部分。
通过 Cloud 控制台
前往 Cloud 控制台中的 Cloud Memorystore 页面(系统可能会提示您提供结算信息)。如果您尚未启用 Memorystore,系统会提示您启用:

启用 Memorystore 服务(可能还需要配置结算信息)后,您会进入 Memorystore 信息中心。您可以在此处查看项目中创建的所有实例。以下项目没有任何资源,因此您会看到“没有要显示的行”。如需创建 Memorystore 实例,请点击顶部的创建实例:

此页面包含一个表单,您可以在其中填写所需的设置来创建 Memorystore 实例:

为了降低本教程及其示例应用的费用,请遵循前面介绍的建议。完成选择后,点击创建。创建过程需要几分钟时间。完成后,复制实例的 IP 地址和端口号以添加到 app.yaml。
从命令行
虽然通过 Cloud 控制台创建 Memorystore 实例在直观性方面具有优势,但有些人更喜欢使用命令行。请务必先安装并初始化 gcloud,然后再继续操作。
与 Cloud 控制台一样,您必须启用 Cloud Memorystore for Redis。发出 gcloud services enable redis.googleapis.com 命令并等待其完成,如以下示例所示:
$ gcloud services enable redis.googleapis.com Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.
如果该服务已启用,则(再次)运行该命令不会产生任何(负面)副作用。启用该服务后,我们来创建一个 Memorystore 实例。该命令如下所示:
gcloud redis instances create NAME --redis-version VERSION \
--region REGION --project PROJECT_ID
为 Memorystore 实例选择一个名称;本实验使用“demo-ms”作为名称,并使用“my-project”作为项目 ID。此示例应用的区域为 us-central1(与 us-central 相同),但如果延迟是一个问题,您可以使用离您更近的区域。您必须选择与 App Engine 应用相同的区域。您可以选择任何喜欢的 Redis 版本,但我们使用之前推荐的版本 5。根据这些设置,您需要运行以下命令(以及相关输出):
$ gcloud redis instances create demo-ms --region us-central1 \
--redis-version redis_5_0 --project my-project
Create request issued for: [demo-ms]
Waiting for operation [projects/my-project/locations/us-central1/operations/operation-xxxx] to complete...done.
Created instance [demo-ms].
与 Cloud 控制台默认设置不同,gcloud 默认使用最少的资源。因此,该命令中既不需要指定服务层级,也不需要指定存储空间量。创建 Memorystore 实例需要几分钟时间,完成后,请记下实例的 IP 地址和端口号,因为它们很快就会添加到 app.yaml 中。
确认实例已创建
通过 Cloud 控制台或命令行
无论您是通过 Cloud 控制台还是命令行创建的实例,都可以使用以下命令确认该实例是否可用并可供使用:gcloud redis instances list --region REGION
以下命令用于检查区域 us-central1 中的实例,以及显示我们刚刚创建的实例的预期输出:
$ gcloud redis instances list --region us-central1 INSTANCE_NAME VERSION REGION TIER SIZE_GB HOST PORT NETWORK RESERVED_IP STATUS CREATE_TIME demo-ms REDIS_5_0 us-central1 BASIC 1 10.aa.bb.cc 6379 default 10.aa.bb.dd/29 READY 2022-01-28T09:24:45
当系统要求您提供实例信息或配置应用时,请务必使用 HOST 和 PORT(而非 RESERVED_IP)。Cloud Console 中的 Cloud Memorystore 信息中心现在应会显示该实例:

从 Compute Engine 虚拟机连接
如果您有 Compute Engine 虚拟机 (VM),还可以从 VM 向 Memorystore 实例发送直接命令,以确认其是否正常运行。请注意,使用虚拟机可能会产生相关费用,而这些费用与您已使用的资源无关。
创建无服务器 VPC 访问通道连接器
与 Cloud Memorystore 类似,您可以在 Cloud 控制台中或通过命令行创建无服务器 Cloud VPC 连接器。同样,Cloud VPC 没有免费层级,因此我们建议您分配最少的资源来完成本 Codelab,以尽可能降低费用,而这些设置可以实现这一目标:
- 选择最低的实例数上限:3(控制台和
gcloud默认值:10) - 选择成本最低的机器类型:
f1-micro(控制台默认:e2-micro,无gcloud默认)
下一部分将引导您使用上述 Cloud VPC 设置通过 Cloud 控制台创建连接器。如果您希望通过命令行进行操作,请跳至下一部分。
通过 Cloud 控制台
在 Cloud 控制台中,前往 Cloud 网络“无服务器 VPC 访问通道”页面(系统可能会提示您提供结算信息)。如果您尚未启用该 API,系统会提示您启用:

启用 API(可能还需要启用结算)后,您会进入显示所有已创建 VPC 连接器的信息中心。下面的屏幕截图中使用的项目没有任何数据,因此系统会显示“No rows to display”(没有要显示的行)。在控制台中,点击顶部的创建连接器:

填写表单,输入所需的设置:

为自己的应用选择合适的设置。对于本教程及其需求极低的示例应用,尽可能降低费用是合理的,因此请遵循前面介绍的建议。完成选择后,点击创建。申请 VPC 连接器需要几分钟才能完成。
通过命令行
在创建 VPC 连接器之前,请先启用无服务器 VPC 访问通道 API。发出以下命令后,您应该会看到类似的输出:
$ gcloud services enable vpcaccess.googleapis.com Operation "operations/acf.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.
启用该 API 后,您可以使用如下命令创建 VPC 连接器:
gcloud compute networks vpc-access connectors create CONNECTOR_NAME \
--range 10.8.0.0/28 --region REGION --project PROJECT_ID
为连接器选择一个名称,以及一个未使用的 /28 CIDR 地址块起始 IP 地址。本教程假定:
- Project ID:
my-project - VPC 连接器名称:
demo-vpc - 实例数下限:2(默认值),实例数上限:3
- 实例类型:
f1-micro - 区域:
us-central1 - IPv4 CIDR 地址块:
10.8.0.0/28(如云控制台中建议的那样)
如果您在执行以下命令时考虑到上述假设,则预期会看到类似于以下内容的输出:
$ gcloud compute networks vpc-access connectors create demo-vpc \
--max-instances 3 --range 10.8.0.0/28 --machine-type f1-micro \
--region us-central1 --project my-project
Create request issued for: [demo-vpc]
Waiting for operation [projects/my-project/locations/us-central1/operations/xxx] to complete...done.
Created connector [demo-vpc].
上述命令省略了指定默认值,例如最小实例数为 2 和名为 default 的网络。创建 VPC 连接器需要几分钟才能完成。
确认连接器已创建
流程完成后,发出以下 gcloud 命令(假设它是区域 us-central1),以确认该服务已创建并可供使用:
$ gcloud compute networks vpc-access connectors list --region us-central1 CONNECTOR_ID REGION NETWORK IP_CIDR_RANGE SUBNET SUBNET_PROJECT MIN_THROUGHPUT MAX_THROUGHPUT STATE demo-vpc us-central1 default 10.8.0.0/28 200 300 READY
同样,信息中心现在应显示您刚刚创建的连接器:

记下 Cloud 项目 ID、VPC 连接器名称和区域。
现在,您已经通过命令行或在控制台中创建了所需的其他 Cloud 资源,接下来需要更新应用配置以支持使用这些资源。
5. 更新配置文件
第一步是更新配置文件,进行所有必要的更改。本 Codelab 的主要目标是帮助 Python 2 用户进行迁移,不过以下各部分通常会提供有关进一步移植到 Python 3 的信息。
requirements.txt
在此部分中,我们添加了支持 Cloud Memorystore 和 Cloud NDB 的软件包。对于 Cloud Memorystore for Redis,只需使用适用于 Python 的标准 Redis 客户端 (redis),因为没有专门的 Cloud Memorystore 客户端库。将 redis 和 google-cloud-ndb 附加到 requirements.txt,并加入来自模块 12 的 flask:
flask
redis
google-cloud-ndb
此 requirements.txt 文件不包含任何版本号,这意味着系统会选择最新版本。如果出现任何不兼容问题,请指定版本号以锁定可正常运行的版本。
app.yaml
要添加的新版块
使用 Cloud NDB 等 Cloud API 时,Python 2 App Engine 运行时需要特定的第三方软件包,即 grpcio 和 setuptools。Python 2 用户必须在 app.yaml 中列出此类内置库以及可用版本。如果您还没有 libraries 部分,请创建一个并添加这两个库,如下所示:
libraries:
- name: grpcio
version: latest
- name: setuptools
version: latest
迁移您的应用时,该应用可能已经包含 libraries 部分。如果存在,但缺少 grpcio 和 setuptools,只需将它们添加到现有的 libraries 部分即可。
接下来,我们的示例应用需要 Cloud Memorystore 实例和 VPC 连接器信息,因此无论您使用的是哪个 Python 运行时,都要向 app.yaml 添加以下两个新部分:
env_variables:
REDIS_HOST: 'YOUR_REDIS_HOST'
REDIS_PORT: 'YOUR_REDIS_PORT'
vpc_access_connector:
name: projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR
以上就是所有必需的更新。更新后的 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
env_variables:
REDIS_HOST: 'YOUR_REDIS_HOST'
REDIS_PORT: 'YOUR_REDIS_PORT'
vpc_access_connector:
name: projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR
以下是“前后对比”示例,说明了您应应用于 app.yaml 的更新:

*Python 3 差异
此部分是可选的,仅在您移植到 Python 3 时适用。为此,您需要对 Python 2 配置进行多项更改。如果您目前不打算升级,请跳过此部分。
Python 3 运行时不使用 threadsafe 和 api_version,因此请删除这两项设置。最新的 App Engine 运行时不支持内置第三方库,也不支持复制非内置库。对于第三方软件包,唯一的要求是在 requirements.txt 中列出它们。因此,可以删除 app.yaml 的整个 libraries 部分。
接下来,Python 3 运行时要求使用自行路由的 Web 框架,因此我们在模块 1 中向开发者展示了如何从 webp2 迁移到 Flask。因此,所有脚本处理程序都必须更改为 auto。由于此应用不提供任何静态文件,因此列出处理程序是“毫无意义”的(因为它们都是 auto),所以也可以移除整个 handlers 部分。因此,经过调整的新缩写版 app.yaml(适用于 Python 3)应缩短为如下所示:
runtime: python39
env_variables:
REDIS_HOST: 'YOUR_REDIS_HOST'
REDIS_PORT: 'YOUR_REDIS_PORT'
vpc_access_connector:
name: projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR
总结了在移植到 Python 3 时 app.yaml 中的差异:
- 删除
threadsafe和api_version设置 - 删除
libraries部分 - 删除
handlers部分(或仅删除script处理程序,如果您的应用传送静态文件)
替换值
Memorystore 和 VPC 连接器的新部分中的值只是占位符。将这些大写值 (YOUR_REDIS_HOST, YOUR_REDIS_PORT, PROJECT_ID, REGION, CONNECTOR_NAME) 替换为您之前创建这些资源时保存的值。对于 Memorystore 实例,请务必使用 HOST(而非 RESERVED_IP)和 PORT。以下是一种快速的命令行方式,可获取 HOST 和 PORT(假设实例名称为 demo-ms,且 REGION 为 us-central1):
$ gcloud redis instances describe demo-ms --region us-central1 \
--format "value(host,port)"
10.251.161.51 6379
如果我们的示例 Redis 实例 IP 地址为 10.10.10.10,使用端口 6379,位于区域 us-central1 中的项目 my-project 中,且 VPC 连接器名称为 demo-vpc,则 app.yaml 中的相应部分将如下所示:
env_variables:
REDIS_HOST: '10.10.10.10'
REDIS_PORT: '6379'
vpc_access_connector:
name: projects/my-project/locations/us-central1/connectors/demo-vpc
创建或更新 appengine_config.py
添加对内置第三方库的支持
与之前处理 app.yaml 的方式一样,添加 grpcio 和 setuptools 库的使用情况。修改 appengine_config.py 以支持内置第三方库。如果您觉得这很熟悉,那是因为在模块 2 中从 App Engine ndb 迁移到 Cloud NDB 时,也需要这样做。所需的具体更改是将 lib 文件夹添加到 setuptools.pkg_resources 工作集中:

*Python 3 差异
此部分是可选的,仅在您移植到 Python 3 时适用。App Engine 第二代的一项重大变化是,不再需要在 app.yaml 中复制(有时称为“供应商化”)(非内置) 第三方软件包和引用内置第三方软件包,这意味着您可以删除整个 appengine_config.py 文件。
6. 更新应用文件
只有一个应用文件 main.py,因此本部分中的所有更改都只会影响该文件。我们以图示方式展示了将此应用迁移到 Cloud Memorystore 的过程中将要进行的更改。它仅用于说明,不适合您仔细分析。所有工作都在于我们对代码所做的更改。

我们先从顶部开始,逐个部分地解决这些问题。
更新了导入
模块 12 的 main.py 中的导入部分使用了 Cloud NDB 和 Cloud Tasks;以下是它们的导入:
之前:
from flask import Flask, render_template, request
from google.appengine.api import memcache
from google.appengine.ext import ndb
切换到 Memorystore 需要读取环境变量,这意味着我们需要 Python os 模块以及 Python Redis 客户端 redis。由于 Redis 无法缓存 Python 对象,因此请使用 pickle 对最近访问列表进行序列化,因此也需要导入该模块。Memcache 的一个优势是,对象序列化会自动进行,而 Memorystore 则更偏向“DIY”。最后,将 App Engine ndb 升级到 Cloud NDB,方法是将 google.appengine.ext.ndb 替换为 google.cloud.ndb。完成这些更改后,您的导入现在应如下所示:
升级后:
import os
import pickle
from flask import Flask, render_template, request
from google.cloud import ndb
import redis
更新初始化
模块 12 的初始化包括实例化 Flask 应用对象 app 和设置一个常量,用于表示一小时的缓存时间:
之前:
app = Flask(__name__)
HOUR = 3600
使用 Cloud API 需要客户端,因此请在 Flask 之后立即实例化 Cloud NDB 客户端。接下来,从您在 app.yaml 中设置的环境变量中获取 Memorystore 实例的 IP 地址和端口号。掌握这些信息后,实例化 Redis 客户端。以下是更新后的代码:
升级后:
app = Flask(__name__)
ds_client = ndb.Client()
HOUR = 3600
REDIS_HOST = os.environ.get('REDIS_HOST', 'localhost')
REDIS_PORT = os.environ.get('REDIS_PORT', '6379')
REDIS = redis.Redis(host=REDIS_HOST, port=REDIS_PORT)
*Python 3 迁移
本部分是可选的,如果您是从模块 12 应用的 Python 3 版本开始,则需要进行多项与导入和初始化相关的更改。
首先,由于 Memcache 是一项 App Engine 捆绑服务,因此在 Python 3 应用中使用它需要 App Engine SDK,具体来说,需要封装 WSGI 应用(以及其他必要配置):
之前:
from flask import Flask, render_template, request
from google.appengine.api import memcache, wrap_wsgi_app
from google.appengine.ext import ndb
app = Flask(__name__)
app.wsgi_app = wrap_wsgi_app(app.wsgi_app)
HOUR = 3600
由于我们要迁移到 Cloud Memorystore(不是像 Memcache 这样的 App Engine 捆绑服务),因此必须移除 SDK 用法。这很简单,您只需删除导入 memcache 和 wrap_wsgi_app 的整行代码即可。同时删除调用 wrap_wsgi_app() 的行。这些更新使应用的这一部分(实际上是整个应用)与 Python 2 版本完全相同。
升级后:
import os
import pickle
from flask import Flask, render_template, request
from google.cloud import ndb
import redis
app = Flask(__name__)
ds_client = ndb.Client()
HOUR = 3600
REDIS_HOST = os.environ.get('REDIS_HOST', 'localhost')
REDIS_PORT = os.environ.get('REDIS_PORT', '6379')
REDIS = redis.Redis(host=REDIS_HOST, port=REDIS_PORT)
最后,从 app.yaml(删除 app_engine_apis: true 这一行)和 requirements.txt(删除 appengine-python-standard 这一行)中移除 SDK 的使用。
迁移到 Cloud Memorystore(和 Cloud NDB)
Cloud NDB 的数据模型旨在与 App Engine ndb 的数据模型兼容,这意味着 Visit 对象的定义保持不变。与模块 2 迁移到 Cloud NDB 类似,store_visit() 和 fetch_visits() 中的所有 Datastore 调用都经过了扩充并嵌入到新的 with 块中(因为需要使用 Cloud NDB 上下文管理器)。以下是该变更之前的这些调用:
之前:
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
def fetch_visits(limit):
'get most recent visits'
return Visit.query().order(-Visit.timestamp).fetch(limit)
向这两个函数添加一个 with ds_client.context() 代码块,并将 Datastore 调用放在该代码块内(并缩进)。在这种情况下,无需对调用本身进行任何更改:
升级后:
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
def fetch_visits(limit):
'get most recent visits'
with ds_client.context():
return Visit.query().order(-Visit.timestamp).fetch(limit)
接下来,我们来看看缓存方面的变化。以下是模块 12 中的 main() 函数:
之前:
@app.route('/')
def root():
'main application (GET) handler'
# check for (hour-)cached visits
ip_addr, usr_agt = request.remote_addr, request.user_agent
visitor = '{}: {}'.format(ip_addr, usr_agt)
visits = memcache.get('visits')
# register visit & run DB query if cache empty or new visitor
if not visits or visits[0].visitor != visitor:
store_visit(ip_addr, usr_agt)
visits = list(fetch_visits(10))
memcache.set('visits', visits, HOUR) # set() not add()
return render_template('index.html', visits=visits)
Redis 具有“get”和“set”调用,与 Memcache 类似。我们只需交换相应的客户端库,对吗?很接近。如前所述,我们无法使用 Redis 缓存 Python 列表(因为需要先进行序列化,而 Memcache 会自动处理此问题),因此在 set() 调用中,使用 pickle.dumps() 将访问次数“pickle”到字符串中。同样,从缓存中检索访问时,您需要在 get() 之后立即使用 pickle.loads() 对其进行反序列化。以下是实现这些更改后的主要处理程序:
升级后:
@app.route('/')
def root():
'main application (GET) handler'
# check for (hour-)cached visits
ip_addr, usr_agt = request.remote_addr, request.user_agent
visitor = '{}: {}'.format(ip_addr, usr_agt)
rsp = REDIS.get('visits')
visits = pickle.loads(rsp) if rsp else None
# register visit & run DB query if cache empty or new visitor
if not visits or visits[0].visitor != visitor:
store_visit(ip_addr, usr_agt)
visits = list(fetch_visits(10))
REDIS.set('visits', pickle.dumps(visits), ex=HOUR)
return render_template('index.html', visits=visits)
至此,main.py 将示例应用对 Memcache 的使用转换为 Cloud Memorystore 所需的更改已全部完成。HTML 模板和移植到 Python 3 方面呢?
更新 HTML 模板文件并移植到 Python 3?
惊喜驾到!由于应用设计为可在 Python 2 和 3 上运行,无需任何代码更改或兼容性库,因此此处无需执行任何操作。您会看到 main.py。在 mod13a (2.x) 和 mod13b (3.x)“完成”文件夹中完全相同。requirements.txt 也一样,只是版本号(如果使用)可能有所不同。由于界面保持不变,因此 templates/index.html 也不会有任何更新。
在配置中,我们已完成在 Python 3 App Engine 上运行此应用所需的一切操作:从 app.yaml 中移除了不必要的指令,并删除了 appengine_config.py 和 lib 文件夹,因为它们在 Python 3 中未使用。
7. 总结/清理
本部分将通过部署应用来结束此 Codelab,并验证应用是否按预期运行以及是否在任何反映的输出中正常运行。应用验证完成后,请执行任何清理操作,并考虑后续步骤。
部署并验证应用
最后一步检查始终是部署示例应用。Python 2 开发者:使用以下命令删除并重新安装 lib。(如果您的系统上同时安装了 Python 2 和 3,您可能需要明确运行 pip2。)
rm -rf ./lib pip install -t lib -r requirements.txt
Python 2 和 3 开发者现在都应使用以下命令部署应用:
gcloud app deploy
由于您只是在幕后重新连接了完全不同的缓存服务,因此应用本身应与模块 12 应用的运行方式完全相同:

此步骤完成了 Codelab。我们建议您将更新后的示例应用与模块 13 的任一文件夹(mod13a [Python 2] 或 mod13b [Python 3])进行比较。
清理
常规
如果您暂时不想继续操作,建议您停用 App Engine 应用,以免产生结算费用。不过,如果您想进一步测试或实验,App Engine 平台有免费配额,因此只要您不超过该使用层级,就不会产生费用。这是计算费用,但相关 App Engine 服务也可能会产生费用,因此请查看其价格页面了解详情。如果此迁移涉及其他 Cloud 服务,则这些服务会单独计费。在任何一种情况下,如果适用,请参阅下文中的“本 Codelab 特有的问题”部分。
为了完全公开透明,我们在此说明,部署到 Google Cloud 无服务器计算平台(例如 App Engine)会产生少量 build 和存储费用。Cloud Build 和 Cloud Storage 都有各自的免费配额。存储该图片会占用部分配额。不过,您可能居住在没有此类免费层的地区,因此请注意存储空间用量,以尽可能减少潜在费用。您应查看的特定 Cloud Storage“文件夹”包括:
console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/imagesconsole.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com- 上述存储链接取决于您的
PROJECT_ID和 *LOC*ation,例如,如果您的应用托管在美国,则为“us”。
另一方面,如果您不打算继续学习此应用或其他相关迁移 Codelab,并且想要彻底删除所有内容,请关闭您的项目。
此 Codelab 特有的
以下列出的服务是此 Codelab 特有的。如需了解详情,请参阅各个产品的文档:
- Cloud Memorystore 需要实例,没有免费层级;如需详细了解使用费用,请参阅其价格页面。
- Cloud 无服务器 VPC 访问通道连接器需要实例,并且没有免费层级;如需详细了解使用费用,请参阅 Cloud VPC 价格页面上的相关部分。
- Cloud Datastore(Datastore 模式的 Cloud Firestore)提供免费层级;如需了解详情,请参阅其价格页面。
本教程涉及使用四种 Cloud 产品:
- App Engine
- Cloud Datastore
- Cloud Memorystore
- Cloud VPC
以下说明介绍了如何释放这些资源,以避免或尽量减少结算费用。
关闭 Memorystore 实例和 VPC 连接器
以下产品没有免费层级,因此您目前正在产生费用。如果您不关闭 Cloud 项目(请参阅下一部分),则必须删除 Memorystore 实例和 VPC 连接器,才能停止结算。与创建这些资源时类似,您也可以通过 Cloud 控制台或命令行来释放这些资源。
通过 Cloud 控制台
如需删除 Memorystore 实例,请返回 Memorystore 信息中心,然后点击相应实例 ID:

进入相应实例的详情页面后,点击“删除”并确认:
如需删除 VPC 连接器,请前往其信息中心,选中要删除的连接器旁边的复选框,然后点击“删除”并确认:

从命令行
以下一对 gcloud 命令分别用于删除 Memorystore 实例和 VPC 连接器:
gcloud redis instances delete INSTANCE --region REGIONgcloud compute networks vpc-access connectors delete CONNECTOR --region REGION
如果您尚未通过 gcloud config set project 设置项目 ID,可能需要提供 --project PROJECT_ID。如果您的 Memorystore 实例名为 demo-ms,VPC 连接器名为 demo-vpc,并且两者都位于 us-central1 区域中,请运行以下命令对并确认:
$ gcloud redis instances delete demo-ms --region us-central1 You are about to delete instance [demo-ms] in [us-central1]. Any associated data will be lost. Do you want to continue (Y/n)? Delete request issued for: [demo-ms] Waiting for operation [projects/PROJECT/locations/REGION/operations/operation-aaaaa-bbbbb-ccccc-ddddd] to complete...done. Deleted instance [demo-ms]. $ $ gcloud compute networks vpc-access connectors delete demo-vpc --region us-central1 You are about to delete connector [demo-vpc] in [us-central1]. Any associated data will be lost. Do you want to continue (Y/n)? Delete request issued for: [demo-vpc] Waiting for operation [projects/PROJECT/locations/REGION/operations/aaaaa-bbbb-cccc-dddd-eeeee] to complete...done. Deleted connector [demo-vpc].
每个请求都需要几分钟才能运行。如果您选择按前文所述关闭整个 Cloud 项目,则可以跳过这些步骤,但您仍需支付结算费用,直到关闭流程完成为止。
后续步骤
除了本教程之外,还有其他迁移模块重点介绍如何从旧版捆绑服务迁出,您可以考虑使用这些模块,包括:
- 模块 2:从 App Engine
ndb迁移到 Cloud NDB - 模块 7-9:从 App Engine 任务队列推送任务迁移到 Cloud Tasks
- 模块 12-13:从 App Engine Memcache 迁移到 Cloud Memorystore
- 模块 15-16:从 App Engine Blobstore 迁移到 Cloud Storage
- 模块 18-19:从 App Engine 任务队列(拉取任务)迁移到 Cloud Pub/Sub
App Engine 不再是 Google Cloud 中唯一的无服务器平台。如果您有一个小型 App Engine 应用或功能有限的应用,并希望将其转换为独立的微服务,或者您希望将单体式应用拆分为多个可重用的组件,那么这些都是考虑迁移到 Cloud Functions 的充分理由。如果容器化已成为应用开发工作流的一部分,尤其是当它包含 CI/CD(持续集成/持续交付或部署)流水线时,请考虑迁移到 Cloud Run。以下模块涵盖了这些使用场景:
- 从 App Engine 迁移到 Cloud Functions:请参阅模块 11
- 从 App Engine 迁移到 Cloud Run:请参阅模块 4,了解如何使用 Docker 将应用容器化;或参阅模块 5,了解如何在不使用容器、Docker 知识或
Dockerfile的情况下完成迁移
您可以选择改用其他无服务器平台,但我们建议您先考虑最适合您的应用和使用情形的选项,然后再进行任何更改。
无论您接下来考虑哪个迁移模块,都可以在 开源代码库中访问所有无服务器迁移站内容(Codelab、视频、源代码 [如有])。该代码库的 README 还提供了有关应考虑哪些迁移以及任何相关的迁移模块“顺序”的指南。
8. 其他资源
下面列出了其他资源,可供开发者进一步探索本迁移模块或相关迁移模块以及相关产品。这包括提供有关此内容的反馈的途径、指向代码的链接,以及您可能会觉得有用的各种文档。
Codelab 问题/反馈
如果您发现本 Codelab 存在任何问题,请先搜索您的问题,然后再提交。用于搜索和创建新问题的链接:
迁移时可参考的资源
下表中提供了指向模块 12(开始)和模块 13(完成)的代码库文件夹的链接。您还可以从所有 App Engine Codelab 迁移的代码库中访问这些示例,您可以克隆该代码库或下载 ZIP 文件。
Codelab | Python 2 | Python 3 |
第 13 模块(本 Codelab) |
在线参考
以下是一些可能与本教程相关的在线资源:
App Engine
- App Engine 文档
- Python 2 App Engine(标准环境)运行时
- 在 Python 2 App Engine 上使用 App Engine 内置库
- Python 3 App Engine(标准环境)运行时
- Python 2 和 3 App Engine(标准环境)运行时之间的差异
- Python 2 到 3 App Engine(标准环境)迁移指南
- App Engine 价格和配额信息
App Engine NDB 和 Cloud NDB
- App Engine NDB 概览
- App Engine NDB Datastore 用法
- Google Cloud NDB 文档
- Google Cloud NDB 代码库
- Cloud Datastore 价格信息
App Engine Memcache 和 Cloud Memorystore
- App Engine Memcache 概览
- Python 2 App Engine
memcache参考文档 - Python 3 App Engine
memcache参考文档 - App Engine
memcache到 Cloud Memorystore 的迁移指南 - Cloud Memorystore 文档
- Cloud Memorystore for Redis 文档
- Cloud Memorystore for Redis 价格信息
- Cloud Memorystore 支持的 Redis 版本
- Cloud Memorystore 首页
- 在 Cloud 控制台中创建新的 Memorystore 实例
- Python Redis 客户端主页
- Python Redis 客户端库文档
Cloud VPC
其他云信息
- 在 Google Cloud Platform 上使用 Python 应用
- Google Cloud Python 客户端库
- Google Cloud“始终免费”层级
- Google Cloud SDK(
gcloud命令行工具) - 所有 Google Cloud 文档
许可
此作品已获得 Creative Commons Attribution 2.0 通用许可授权。
