使用 Firestore 处理会话


本教程介绍如何在 App Engine 上处理会话。

许多应用需要处理会话以进行身份验证和设定用户偏好设置。 Gorilla Web Toolkit sessions 软件包附带一个基于文件系统的实现,可实现此功能。但此实现不适用于可以通过多个实例提供的应用,因为各实例之间记录的会话可能不同。gorilla/sessions 软件包还附带一个基于 Cookie 的实现。但此实现需要加密 Cookie 并将整个会话存储在客户端上,而不仅仅是执行加密和存储会话 ID,整个会话对于某些应用而言可能过大。

目标

  • 编写应用。
  • 在本地运行应用。
  • 在 App Engine 上部署应用。

费用

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

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

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

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

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

  4. 启用 Firestore API。

    启用 API

  5. 安装 Google Cloud CLI。
  6. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  7. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

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

  9. 启用 Firestore API。

    启用 API

  10. 安装 Google Cloud CLI。
  11. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  12. 更新 gcloud 组件:
    gcloud components update
  13. 准备开发环境

设置项目

  1. 在终端窗口中,将示例应用代码库克隆到本地机器:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
  2. 转到包含示例代码的目录:

    cd golang-samples/getting-started/sessions

了解 Web 应用

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

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

您需要先存储会话中当前用户的相关信息,然后应用才能拿存储用户偏好设置。此示例应用使用 Firestore 来存储会话数据。

您可以使用与 gorilla/sessions 兼容的会话存储库 firestoregorilla

  1. 应用首先导入依赖项,指定用于保存 sessions.Store 和 HTML 模板的 app 类型,并指定问候语列表。

    
    // Command sessions starts an HTTP server that uses session state.
    package main
    
    import (
    	"context"
    	"fmt"
    	"html/template"
    	"log"
    	"math/rand"
    	"net/http"
    	"os"
    
    	"cloud.google.com/go/firestore"
    	firestoregorilla "github.com/GoogleCloudPlatform/firestore-gorilla-sessions"
    	"github.com/gorilla/sessions"
    )
    
    // app stores a sessions.Store. Create a new app with newApp.
    type app struct {
    	store sessions.Store
    	tmpl  *template.Template
    }
    
    // greetings are the random greetings that will be assigned to sessions.
    var greetings = []string{
    	"Hello World",
    	"Hallo Welt",
    	"Ciao Mondo",
    	"Salut le Monde",
    	"Hola Mundo",
    }
    
  2. 接下来,该应用会定义 main 函数,以创建新的 app 实例、注册索引处理程序并启动 HTTP 服务器。newApp 函数使用 firestoregorilla.New 函数初始化 sessions.Store,以创建 app 实例。

    
    func main() {
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    	projectID := os.Getenv("GOOGLE_CLOUD_PROJECT")
    	if projectID == "" {
    		log.Fatal("GOOGLE_CLOUD_PROJECT must be set")
    	}
    
    	a, err := newApp(projectID)
    	if err != nil {
    		log.Fatalf("newApp: %v", err)
    	}
    
    	http.HandleFunc("/", a.index)
    
    	log.Printf("Listening on port %s", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    // newApp creates a new app.
    func newApp(projectID string) (*app, error) {
    	ctx := context.Background()
    	client, err := firestore.NewClient(ctx, projectID)
    	if err != nil {
    		log.Fatalf("firestore.NewClient: %v", err)
    	}
    	store, err := firestoregorilla.New(ctx, client)
    	if err != nil {
    		log.Fatalf("firestoregorilla.New: %v", err)
    	}
    
    	tmpl, err := template.New("Index").Parse(`<body>{{.views}} {{if eq .views 1.0}}view{{else}}views{{end}} for "{{.greeting}}"</body>`)
    	if err != nil {
    		return nil, fmt.Errorf("template.New: %w", err)
    	}
    
    	return &app{
    		store: store,
    		tmpl:  tmpl,
    	}, nil
    }
    
  3. 索引处理程序获取用户的会话,如果需要的话,可创建一个会话。系统会为新会话分配一种随机语言,此时视图计数为 0。然后,视图计数会加 1,系统会保存会话,HTML 模板会写入响应。

    
    // index uses sessions to assign users a random greeting and keep track of
    // views.
    func (a *app) index(w http.ResponseWriter, r *http.Request) {
    	if r.RequestURI != "/" {
    		return
    	}
    
    	// name is a non-empty identifier for this app's sessions. Set it to
    	// something descriptive for your app. It is used as the Firestore
    	// collection name that stores the sessions.
    	name := "hello-views"
    	session, err := a.store.Get(r, name)
    	if err != nil {
    		// Could not get the session. Log an error and continue, saving a new
    		// session.
    		log.Printf("store.Get: %v", err)
    	}
    
    	if session.IsNew {
    		// firestoregorilla uses JSON, which unmarshals numbers as float64s.
    		session.Values["views"] = float64(0)
    		session.Values["greeting"] = greetings[rand.Intn(len(greetings))]
    	}
    	session.Values["views"] = session.Values["views"].(float64) + 1
    	if err := session.Save(r, w); err != nil {
    		log.Printf("Save: %v", err)
    		// Don't return early so the user still gets a response.
    	}
    
    	if err := a.tmpl.Execute(w, session.Values); err != nil {
    		log.Printf("Execute: %v", err)
    	}
    }
    

    下图说明了 Firestore 如何处理 App Engine 应用的会话。

    架构图:用户、App Engine、Firestore。

删除会话

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

在本地运行

  1. 在终端窗口中,构建 sessions 二进制文件:

    go build
    
  2. 启动 HTTP 服务器:

    ./sessions
    
  3. 查看网络浏览器中的应用:

    Cloud Shell

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

    本地机器

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

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

    Google Cloud 控制台中的 Firestore 会话。

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

在 App Engine 上部署并运行

通过 App Engine 标准环境,您可以构建和部署在繁重负载和大量数据的压力下仍能可靠运行的应用。

本教程使用 App Engine 标准环境来部署服务器。

app.yaml 文件包含 App Engine 标准环境配置:

runtime: go112
  1. 在 App Engine 上部署应用:

    gcloud app deploy
    
  2. 在浏览器中,输入以下网址:

    https://PROJECT_ID.REGION_ID.r.appspot.com

    替换以下内容:

现在,问候语由在 App Engine 实例上运行的 Web 服务器提供。

调试应用

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

  1. 检查 gcloud 部署命令是否已成功完成,并且未输出任何错误。如果存在错误(例如 message=Build failed),修复问题,然后再次尝试部署 App Engine 应用
  2. 在 Google Cloud 控制台中,转到 Logs Explorer 页面。

    转到“Logs Explorer”页面

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

    2. 确保已启用 Firestore API。

清理

删除项目

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

    转到“管理资源”

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

删除 App Engine 实例

  1. 在 Google Cloud 控制台中,转到 App Engine 的版本页面。

    转到“版本”

  2. 选中要删除的非默认应用版本对应的复选框。
  3. 如需删除应用版本,请点击删除

后续步骤