使用命名空间实现多租户

借助 Namespaces API,您可以在应用中轻松实现多租户功能,只需使用 软件包在 中为每个租户选择命名空间字符串即可。

设置当前命名空间

您可以使用 获取、设置和验证命名空间。通过命名空间管理器,您能够为支持命名空间的 API 设置当前命名空间。您可以使用 预先设置当前命名空间,数据存储区和 Memcache 会自动使用该命名空间。

大多数 App Engine 开发者会使用其 Google Workspace(以前称为 G Suite)网域作为当前命名空间。借助 Google Workspace,您可以将应用部署到您拥有的任何网域,从而可轻松利用此机制为不同网域配置不同的命名空间。然后,您可以使用这些单独的命名空间分隔各个网域中的数据。如需详细了解如何在 Google Workspace 信息中心中设置多个网域,请参阅在 Google Workspace 网址上部署应用

以下代码示例演示了如何将当前命名空间设置为用于映射网址的 Google Workspace 网域。值得注意的是,对于通过同一 Google Workspace 网域映射的所有网址,此字符串是相同的。

如果您没有为 namespace 指定值,命名空间将设置为空字符串。namespace 可以是任意字符串,但只能包含字母数字字符、句点、下划线和连字符且长度不超过 100 个字符。更明确地说,命名空间字符串必须与正则表达式 [0-9A-Za-z._-]{0,100} 匹配。

按照惯例,所有以“_”(下划线)开头的命名空间将保留给系统使用。该系统命名空间规则不强制实施,但若不遵循,则很可能造成难以预料的负面影响。

避免数据泄露

与多租户应用相关的一个常见风险是,命名空间中可能会发生数据泄露。导致数据意外泄露的原因有很多,其中包括:

  • 将命名空间与尚不支持命名空间的 App Engine API 一起使用。例如,Blobstore 不支持命名空间。如果将命名空间与 Blobstore 一起使用,需要避免对最终用户请求使用 Blobstore 查询,并避免使用来自不可信来源的 Blobstore 键。
  • 通过 URL Fetch 或其他机制使用外部存储介质(而不是 Memcache 和数据存储区),但并未为命名空间提供划分方案。
  • 根据用户的电子邮件网域设置命名空间。大多数情况下,您并不希望一个网域中的所有电子邮件地址都访问同一个命名空间。此外,使用电子邮件网域时,在用户登录后您的应用才可使用命名空间。

部署命名空间

以下部分介绍了如何使用其他 App Engine 工具和 API 来部署命名空间。

根据用户创建命名空间

部分应用需要根据用户来创建命名空间。如果要在用户级别为已登录用户划分数据,请考虑使用 user.Current(c).ID,它会为用户返回唯一的永久 ID。以下代码示例演示了如何使用 Users API 实现此目的:

通常,基于用户创建命名空间的应用还会为不同用户提供特定的着陆页。在这些情况下,应用需要提供一个网址架构,以指示向用户显示哪个着陆页。

将命名空间与数据存储区一起使用

默认情况下,数据存储区使用当前命名空间来处理数据存储区请求。在创建 datastore.Key 对象时,此 API 将该当前命名空间应用于这些对象。因此,如果应用在序列化表单中存储 Key 对象,您需要特别小心,因为命名空间将保留在这些序列化内容中。

如果您使用非序列化的 Key 对象,请确保这些对象按预期方式工作。通过在调用任何数据存储区 API 之前设置当前命名空间,大多数使用数据存储区 (put/query/get) 而未使用其他存储机制的简单应用将按预期方式工作。

对于命名空间,QueryKey 对象会表现出以下独特行为:

  • QueryKey 对象构造完毕后,将继承当前命名空间,除非您设置了明确的命名空间。
  • 当应用通过祖先实体创建新的 Key 时,新 Key 将继承祖先实体的命名空间。

将命名空间与 Memcache 一起使用

默认情况下,Memcache 会使用命名空间管理器中的当前命名空间来处理 Memcache 请求。大多数情况下,您不需要在 Memcache 中明确设置命名空间,那样做可能会产生意外错误。

不过,在一些特殊情况下,您可能需要在 Memcache 中明确设置命名空间。例如,应用可能在所有命名空间之间共享相同的数据(例如,包含国家/地区代码的表格)。

以下代码段演示了如何在 Memcache 中明确设置命名空间:

将命名空间与任务队列一起使用

默认情况下,推送队列会使用创建任务时在命名空间管理器中设置的当前命名空间。大多数情况下,您不需要在任务队列中明确设置命名空间,这么做可能会导致产生意外的错误。

任务名称会在所有命名空间中共享。即使任务使用不同的命名空间,也不能同名。如果要对多个命名空间使用相同的任务名称,您只需将每个命名空间附加到任务名称中。

当新任务调用任务队列 方法时,任务队列将从命名空间管理器复制当前命名空间和(如果适用)Google Workspace 网域。执行任务后,系统将恢复当前命名空间和 Google Workspace 命名空间。

在一些特殊情况下,您需要为在所有命名空间中执行的任务明确设置命名空间。例如,您可以创建一个任务,以汇总所有命名空间中使用情况的统计信息。然后,您可以明确设置该任务的命名空间。以下代码示例演示了如何使用任务队列明确设置命名空间。

将命名空间与 Blobstore 一起使用

Blobstore 不按命名空间划分。要在 Blobstore 中保留命名空间,您需要通过支持命名空间的存储介质(当前只有 Memcache、数据存储区和任务队列)访问 Blobstore。例如,如果 blob 的 Key 存储在数据存储区实体中,您可以使用支持命名空间的数据存储区 Key 访问该实体。

如果应用通过支持命名空间的存储空间中存储的键访问 Blobstore,则 Blobstore 本身无需按命名空间进行划分。应用必须通过以下方法避免命名空间之间的 blob 泄露:

  • 不使用 BlobInfo 来处理最终用户请求。您可以对管理请求(例如,生成关于所有应用 Blob 的报告)使用 BlobInfo 查询,但对最终用户请求使用该查询可能会导致数据泄露,因为并非所有 BlobInfo 记录都按命名空间划分。
  • 不使用来自不可信来源的 Blobstore 键。

为数据存储区查询设置命名空间

在 Google Cloud Console 中,您可以为 Datastore 查询设置命名空间

如果不想使用默认值,请从下拉列表中选择要使用的命名空间。

将命名空间与批量加载器一起使用

批量加载器支持 --namespace=NAMESPACE 标志,后者可用于指定要使用的命名空间。每个命名空间都需要单独处理;如果要访问所有命名空间,则需要依次处理这些命名空间。