使用 Firestore 处理会话

本教程介绍如何在 Cloud Run 上处理会话。

许多应用需要处理身份验证和用户偏好设置的会话。Jetty 框架附带一个基于内存的实现,可执行此功能。但此实现不适用于可以通过多个实例提供的应用,因为各实例之间记录的会话可能不同。本教程介绍如何在 Cloud Run 上处理会话。

目标

  • 编写应用。
  • 在本地运行应用。
  • 在 Cloud Run 上部署应用。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. 确保您的 Google Cloud 项目已启用结算功能

  4. 启用 Firestore API。

    启用 API

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  6. 确保您的 Google Cloud 项目已启用结算功能

  7. 启用 Firestore API。

    启用 API

  8. 在 Google Cloud Console 中,通过 Cloud Shell 打开此应用。

    转到 Cloud Shell

    利用 Cloud Shell,您可以直接在浏览器中通过命令行访问云端资源。在浏览器中打开 Cloud Shell,然后点击继续下载示例代码并切换到应用目录。

  9. 在 Cloud Shell 中,配置 gcloud CLI 以使用新的 Google Cloud 项目:
    # Configure gcloud for your project
    gcloud config set project YOUR_PROJECT_ID
    
  10. 默认情况下,更新 Maven 以使用 Java 11:
    sudo update-alternatives --config java
    
    系统提示时,输入数字以选择 Java 11。记下该版本列出的路径。
  11. 将您在上一步中复制的路径导出为环境变量:
    export JAVA_HOME=java-11-path
    

Web 应用

此应用为每位用户显示不同语言的问候语。回访用户始终会收到同一种语言的问候语。

多个应用窗口显示不同语言的问候语。

在应用存储用户偏好设置之前,您需要存储会话中当前用户的相关信息。此示例应用使用 WebFilter 来检索和更新 Firestore 中的会话数据。

@Override
public void init(FilterConfig config) throws ServletException {
  // Initialize local copy of datastore session variables.
  firestore = FirestoreOptions.getDefaultInstance().getService();
  sessions = firestore.collection("sessions");

  try {
    // Delete all sessions unmodified for over two days.
    Calendar cal = Calendar.getInstance();
    cal.setTime(new Date());
    cal.add(Calendar.HOUR, -48);
    Date twoDaysAgo = Calendar.getInstance().getTime();
    QuerySnapshot sessionDocs =
        sessions.whereLessThan("lastModified", dtf.format(twoDaysAgo)).get().get();
    for (QueryDocumentSnapshot snapshot : sessionDocs.getDocuments()) {
      snapshot.getReference().delete();
    }
  } catch (InterruptedException | ExecutionException e) {
    throw new ServletException("Exception initializing FirestoreSessionFilter.", e);
  }
}

下图说明 Firestore 如何处理 Cloud Run 应用的会话。

架构图:用户、Cloud Run、Firestore。

HttpServletRequest 使用 Cookie 来存储本地会话的唯一 ID,该 ID 对应于 Firestore 中带有会话详细信息的文档。

删除会话

Firestore 不会删除旧的或已过期的会话。您可以在 Google Cloud Console删除会话数据,或实施自动删除策略。如果您为会话使用 Memcache 或 Redis 等存储解决方案,系统会自动删除已过期的会话。

在本地运行

  1. 启动 HTTP 服务器:

    mvn jetty:run
    
  2. 查看网络浏览器中的应用:

    Cloud Shell

    在 Cloud Shell 工具栏中,点击 Web 预览 Web 预览,然后选择在端口 8080 上预览

    本地机器

    在浏览器中,转到 http://localhost:8080

    您会看到五个问候语之一:“Hello World”、“Hallo Welt”、“Hola mundo”、“Salut le Monde”或“Ciao Mondo”。如果您使用其他浏览器或无痕模式打开网页,语言会更改。您可以在 Google Cloud Console 中查看和修改会话数据。

    Cloud Console 中的 Firestore 会话。

  3. 要停止 HTTP 服务器,请在终端窗口中按 Control+C

在 Cloud Run 上部署和运行

您可以使用 Cloud Run 来构建和部署可在负载繁重和具有大量数据的情况下可靠运行的应用。

  1. 在终端窗口中,使 用Jib Maven 插件构建代码映像并将其部署到 Google Container Registry (GCR)

    mvn clean package jib:build

  2. 将应用部署到 Cloud Run:

       gcloud beta run deploy session-handling --image gcr.io/MY_PROJECT/session-handling 
    --platform managed --region us-central1 --memory 512M

    MY_PROJECT 替换为您创建的 Cloud 项目的 ID。访问此命令返回的网址,以查看会话数据在页面加载之间如何持久存在。

调试应用

如果您无法连接到 Cloud Run 应用,请检查以下内容:

  1. 检查 gcloud 部署命令是否已成功完成,并且未输出任何错误。如果存在错误(例如 message=Build failed),请更正这些错误,然后重试部署 Cloud Run 应用
  2. 在 Cloud Console 中,转到日志浏览器页面。

    转到“日志浏览器”页面

    1. 最近选择的资源下拉列表中,点击 Cloud Run 应用,然后点击所有 module_id。您将看到自您访问应用以来的请求列表。如果您未看到请求列表,请确认您是否已从下拉列表中选择所有 module_id。如果您发现 Cloud Console 出现错误消息,请检查应用代码是否与“编写 Web 应用”相关部分中的代码匹配。

    2. 确保已启用 Firestore API。

清理

删除项目

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除 Cloud Run 实例

从 Cloud Run 中删除服务。

  • 在 Cloud Console 中,转到 Cloud Run 的服务页面。

    转到“服务”页面

  • 选择您要删除的服务。
  • 单击删除 以删除服务。

后续步骤