Skip to content

Latest commit

 

History

History
778 lines (572 loc) · 27.5 KB

File metadata and controls

778 lines (572 loc) · 27.5 KB

十三、无服务器编程

本章将重点介绍无服务器架构,并将其与 Go 语言结合使用。无服务器架构是开发人员不管理后端服务器的架构。这包括亚马逊 Lambda、谷歌应用引擎和 Firebase 等服务。这些服务允许您在 web 上快速部署应用和存储数据。

本章中的所有配方都涉及为使用付费的第三方服务;确保在使用完后进行清理。否则,请将这些方法视为在这些平台上启动更大应用的启动方法。

在本章中,我们将介绍以下配方:

  • 用 Apex 在 Lambda 上编程
  • Apex 无服务器日志记录和度量
  • 谷歌应用引擎与围棋
  • 使用firebase.google.com/go与 Firebase 合作

用 Apex 在 Lambda 上编程

Apex 是用于构建、部署和管理 AWS Lambda 功能的工具。它过去提供 Goshim用于在代码中管理 Lambda 函数,但现在使用本机 AWS 库(完成此操作 https://github.com/aws/aws-lambda-go 。本食谱将探索如何创建 Go Lambda 函数并使用 Apex 部署它们。

准备

根据以下步骤配置您的环境:

  1. 下载 Go 1.12.6 或更高版本并安装到您的操作系统上 https://golang.org/doc/install
  2. 开始安装 Apexhttp://apex.run/#installation
  3. 打开终端或控制台应用,创建并导航到一个项目目录,如~/projects/go-programming-cookbook。我们将在本配方中介绍的所有代码都将在此目录中运行和修改。
  4. 将最新代码克隆到~/projects/go-programming-cookbook-original。在这里,您可以选择从该目录工作,而不是手动键入示例:
$ git clone git@github.com:PacktPublishing/Go-Programming-Cookbook-Second-Edition.git go-programming-cookbook-original

怎么做。。。

这些步骤包括编写和运行应用:

  1. 从终端或控制台应用中,创建一个名为~/projects/go-programming-cookbook/chapter13/lambda的新目录并导航到它。
  2. 运行以下命令:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/lambda 

您应该会看到一个名为go.mod的文件,其中包含以下内容:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/lambda
  1. 创建一个 Amazon 帐户和一个 IAM 角色,可以编辑 Lambda 函数,这可以从完成 https://aws.amazon.com/lambda/
  2. 创建一个名为~/.aws/credentials的文件,其中包含以下内容,复制您在 Amazon 控制台中设置的凭据:
        [default]
        aws_access_key_id = xxxxxxxx
        aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxx
  1. 创建环境变量以保存所需区域:
        export AWS_REGION=us-west-2
  1. 运行apex init命令并按照屏幕上的说明进行操作:
$ apex init 

Enter the name of your project. It should be machine-friendly, as this is used to prefix your functions in Lambda.

Project name: go-cookbook

Enter an optional description of your project.

Project description: Demonstrating Apex with the Go Cookbook

[+] creating IAM go-cookbook_lambda_function role
[+] creating IAM go-cookbook_lambda_logs policy
[+] attaching policy to lambda_function role.
[+] creating ./project.json
[+] creating ./functions

Setup complete, deploy those functions!

$ apex deploy
  1. 删除lambda/functions/hello目录。
  2. 创建具有以下内容的新lambda/functions/greeter1/main.go文件:
package main

import (
  "context"
  "fmt"

  "github.com/aws/aws-lambda-go/lambda"
)

// Message is the input to the function and
// includes a Name
type Message struct {
  Name string `json:"name"`
}

// Response is sent back and contains a greeting
// string
type Response struct {
  Greeting string `json:"greeting"`
}

// HandleRequest will be called when the lambda function is invoked
// it takes a Message and returns a Response that contains a greeting
func HandleRequest(ctx context.Context, m Message) (Response, error) {
  return Response{Greeting: fmt.Sprintf("Hello, %s", m.Name)}, nil
}

func main() {
  lambda.Start(HandleRequest)
}
  1. 创建具有以下内容的新lambda/functions/greeter/main.go文件:
package main

import (
  "context"
  "fmt"

  "github.com/aws/aws-lambda-go/lambda"
)

// Message is the input to the function and
// includes a FirstName and LastName
type Message struct {
  FirstName string `json:"first_name"`
  LastName string `json:"last_name"`
}

// Response is sent back and contains a greeting
// string
type Response struct {
  Greeting string `json:"greeting"`
}

// HandleRequest will be called when the lambda function is invoked
// it takes a Message and returns a Response that contains a greeting
// this greeting contains the first and last name specified
func HandleRequest(ctx context.Context, m Message) (Response, error) {
  return Response{Greeting: fmt.Sprintf("Hello, %s %s", m.FirstName, m.LastName)}, nil
}

func main() {
  lambda.Start(HandleRequest)
}
  1. 部署它们:
$ apex deploycreating function env= function=greeter2creating function env= function=greeter1created alias current env= function=greeter2 version=4function created env= function=greeter2 name=go-cookbook_greeter2 version=1created alias current env= function=greeter1 version=5function created env= function=greeter1 name=go-cookbook_greeter1 version=1
  1. 调用新部署的函数:
$ echo '{"name": "Reader"}' | apex invoke greeter1 {"greeting":"Hello, Reader"}

$ echo '{"first_name": "Go", "last_name": "Coders"}' | apex invoke greeter2 {"greeting":"Hello, Go Coders"}
  1. 请看一下日志:
$ apex logs greeter2
apex logs greeter2
/aws/lambda/go-cookbook_greeter2 START RequestId: 7c0f9129-3830-11e7-8755-75aeb52a51b9 Version: 1
/aws/lambda/go-cookbook_greeter2 END RequestId: 7c0f9129-3830-11e7-8755-75aeb52a51b9
/aws/lambda/go-cookbook_greeter2 REPORT RequestId: 7c0f9129-3830-11e7-8755-75aeb52a51b9 Duration: 93.84 ms Billed Duration: 100 ms 
Memory Size: 128 MB Max Memory Used: 19 MB 
  1. 清理已部署的服务:
$ apex delete
The following will be deleted:

- greeter1 - greeter2

Are you sure? (yes/no) yesdeleting env= function=greeterfunction deleted env= function=greeter

它是如何工作的。。。

AWS Lambda 使无需维护服务器即可按需运行功能变得简单。Apex 提供了用于部署、版本控制和测试功能的工具,您可以将这些功能交付给 Lambda。

围棋图书馆(https://github.com/aws/aws-lambda-go 在 Lambda 中提供本机 Go 编译,并允许我们将 Go 代码部署为 Lambda 函数。这是通过定义处理程序、处理传入的请求有效负载和返回响应来实现的。当前,您定义的函数必须遵循以下规则:

  • 处理程序必须是函数。
  • 处理程序可以接受零到两个参数。
  • 如果有两个参数,第一个参数必须满足context.Context接口。
  • 处理程序可能返回零到两个参数。
  • 如果有两个返回值,则第二个参数必须是错误。
  • 如果有一个返回值,那么它一定是一个错误。

在这个配方中,我们定义了两个 greeter 函数,一个取全名,另一个将名字拆分为名字和姓氏。如果我们修改一个函数greeter,而不是创建两个函数,Apex 就会部署新版本,并在前面的所有示例中调用v2而不是v1。也可以使用apex rollback greeter回滚。

Apex 无服务器日志记录和度量

在使用诸如 Lambda 之类的无服务器函数时,具有可移植的结构化日志是很有价值的。此外,您还可以将以前处理日志记录的方法与此方法相结合。我们在第 4 章Go中的错误处理中介绍的方法同样相关。因为我们使用 Apex 来管理 Lambda 函数,所以我们选择使用 Apex 记录器来实现这个配方。我们还将依赖 Apex 以及 AWS 控制台提供的指标。早期的方法探索了更复杂的日志记录和度量示例,这些示例仍然适用于 Apex logger,可以使用 Amazon Kinesis 或 Elasticsearch 轻松配置为聚合日志。

准备

请参阅本章中的Go 编程中的准备部分,该部分是关于使用 Apex配方的 Lambda 的。

怎么做。。。

这些步骤包括编写和运行应用:

  1. 从终端或控制台应用中,创建一个名为~/projects/go-programming-cookbook/chapter13/logging的新目录并导航到它。
  2. 运行以下命令:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/logging 

您应该会看到一个名为go.mod的文件,其中包含以下内容:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/logging
  1. 创建一个 Amazon 帐户和一个 IAM 角色,可以编辑 Lambda 函数,可以在完成 https://aws.amazon.com/lambda/
  2. 创建一个包含以下内容的~/.aws/credentials文件,复制您在 Amazon 控制台中设置的凭据:
        [default]
        aws_access_key_id = xxxxxxxx
        aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxx
  1. 创建环境变量以保存所需区域:
        export AWS_REGION=us-west-2
  1. 运行apex init命令并按照屏幕上的说明进行操作:
$ apex init 

Enter the name of your project. It should be machine-friendly, as this is used to prefix your functions in Lambda.

Project name: logging 

Enter an optional description of your project.

Project description: An example of apex logging and metrics

[+] creating IAM logging_lambda_function role
[+] creating IAM logging_lambda_logs policy
[+] attaching policy to lambda_function role.
[+] creating ./project.json
[+] creating ./functions

Setup complete, deploy those functions!

$ apex deploy
  1. 删除lambda/functions/hello目录。
  2. 创建具有以下内容的新lambda/functions/secret/main.go文件:
package main

import (
  "context"
  "os"

  "github.com/apex/log"
  "github.com/apex/log/handlers/text"
  "github.com/aws/aws-lambda-go/lambda"
)

// Input takes in a secret
type Input struct {
  Secret string `json:"secret"`
}

// HandleRequest will be called when the Lambda function is invoked
// it takes an input and checks if it matches our super secret value
func HandleRequest(ctx context.Context, input Input) (string, error) {
  log.SetHandler(text.New(os.Stderr))

  log.WithField("secret", input.Secret).Info("secret guessed")

  if input.Secret == "klaatu barada nikto" {
    return "secret guessed!", nil
  }
  return "try again", nil
}

func main() {
  lambda.Start(HandleRequest)
}
  1. 将其部署到指定的区域:
$ apex deploycreating function env= function=secretcreated alias current env= function=secret version=1function created env= function=secret name=logging_secret version=1
  1. 要调用它,请运行以下命令:
$ echo '{"secret": "open sesame"}' | apex invoke secret
"try again"

$ echo '{"secret": "klaatu barada nikto"}' | apex invoke secret
"secret guessed!"
  1. 检查日志:
$ apex logs secret
/aws/lambda/logging_secret START RequestId: cfa6f655-3834-11e7-b99d-89998a7f39dd Version: 1
/aws/lambda/logging_secret INFO[0000] secret guessed secret=open sesame
/aws/lambda/logging_secret END RequestId: cfa6f655-3834-11e7-b99d-89998a7f39dd
/aws/lambda/logging_secret REPORT RequestId: cfa6f655-3834-11e7-b99d-89998a7f39dd Duration: 52.23 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 19 MB 
/aws/lambda/logging_secret START RequestId: d74ea688-3834-11e7-aa4e-d592c1fbc35f Version: 1
/aws/lambda/logging_secret INFO[0012] secret guessed secret=klaatu barada nikto
/aws/lambda/logging_secret END RequestId: d74ea688-3834-11e7-aa4e-d592c1fbc35f
/aws/lambda/logging_secret REPORT RequestId: d74ea688-3834-11e7-aa4e-d592c1fbc35f Duration: 7.43 ms Billed Duration: 100 ms 
Memory Size: 128 MB Max Memory Used: 19 MB 
  1. 检查您的指标:
$ apex metrics secret 

secret
total cost: $0.00
invocations: 0 ($0.00)
duration: 0s ($0.00)
throttles: 0
errors: 0
memory: 128
  1. 清理已部署的服务:
$ apex delete
Are you sure? (yes/no) yesdeleting env= function=secretfunction deleted env= function=secret

它是如何工作的。。。

在这个配方中,我们创建了一个名为 secret 的新 Lambda 函数,该函数将响应您是否猜到了一个秘密短语。函数解析传入的 JSON 请求,使用Stderr执行一些日志记录,并返回响应。

在多次使用该函数之后,我们可以看到我们的日志是使用apex logs命令可见的。此命令可以在单个 Lambda 函数上运行,也可以跨所有托管函数运行。如果要将 Apex 命令链接在一起并希望查看多个服务的日志,这一点尤其有用。

此外,我们还向您展示了如何使用apex metrics命令来收集有关应用的常规指标,包括成本和调用。您还可以在 AWS 控制台的 Lambda 部分中直接看到许多此类信息。像其他食谱一样,我们试图在最后清理自己。

谷歌应用引擎与围棋

AppEngine 是一项 Google 服务,它可以帮助快速部署 web 应用。这些应用可以访问云存储和各种其他 Google API。总体思路是,appengine 可以轻松地根据负载进行扩展,并简化与托管应用相关的任何操作管理。此配方将显示如何创建和选择性部署基本应用引擎应用。本食谱不会涉及设置谷歌云帐户、设置账单或清理实例的细节。作为最低要求,访问谷歌云数据存储(https://cloud.google.com/datastore/docs/concepts/overview 是该配方发挥作用所必需的。

准备

根据以下步骤配置您的环境:

  1. 下载 Go 1.11.1 或更高版本并在您的操作系统上安装 https://golang.org/doc/install
  2. 下载谷歌云 SDKhttps://cloud.google.com/appengine/docs/flexible/go/quickstart
  3. 创建允许您执行数据存储访问并记录应用名称的应用。对于这个配方,我们将使用go-cookbook
  4. 安装gcloud components install app-engine-goGo app 发动机组件。
  5. 打开终端或控制台应用,创建并导航到一个项目目录,如~/projects/go-programming-cookbook。我们将在本配方中介绍的所有代码都将在此目录中运行和修改。
  6. 将最新代码克隆到~/projects/go-programming-cookbook-original。在这里,您可以选择从该目录工作,而不是手动键入示例:
$ git clone git@github.com:PacktPublishing/Go-Programming-Cookbook-Second-Edition.git go-programming-cookbook-original

怎么做。。。

这些步骤包括编写和运行应用:

  1. 从终端或控制台应用中,创建一个名为~/projects/go-programming-cookbook/chapter13/appengine的新目录并导航到它。
  2. 运行以下命令:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/appengine 

您应该会看到一个名为go.mod的文件,其中包含以下内容:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/appengine
  1. 创建一个名为app.yml的文件,其中包含以下内容,将go-cookbook替换为您在准备部分中创建的应用的名称:
runtime: go112

manual_scaling:
  instances: 1

#[START env_variables]
env_variables:
  GCLOUD_DATASET_ID: go-cookbook
#[END env_variables]
  1. 创建一个名为message.go的文件,其内容如下:
        package main

        import (
            "context"
            "time"

            "cloud.google.com/go/datastore"
        )

        // Message is the object we store
        type Message struct {
            Timestamp time.Time
            Message string
        }

        func (c *Controller) storeMessage(ctx context.Context, message 
        string) error {
            m := &Message{
                Timestamp: time.Now(),
                Message: message,
            }

            k := datastore.IncompleteKey("Message", nil)
            _, err := c.store.Put(ctx, k, m)
            return err
        }

        func (c *Controller) queryMessages(ctx context.Context, limit 
        int) ([]*Message, error) {
            q := datastore.NewQuery("Message").
            Order("-Timestamp").
            Limit(limit)

            messages := make([]*Message, 0)
            _, err := c.store.GetAll(ctx, q, &messages)
            return messages, err
        }
  1. 创建一个名为controller.go的文件,其内容如下:
        package main

        import (
            "context"
            "fmt"
            "log"
            "net/http"

            "cloud.google.com/go/datastore"
        )

        // Controller holds our storage and other
        // state
        type Controller struct {
            store *datastore.Client
        }

        func (c *Controller) handle(w http.ResponseWriter, r 
        *http.Request) {
            if r.Method != http.MethodGet {
                http.Error(w, "invalid method", 
                http.StatusMethodNotAllowed)
                return
            }

            ctx := context.Background()

            // store the new message
            r.ParseForm()
            if message := r.FormValue("message"); message != "" {
                if err := c.storeMessage(ctx, message); err != nil {
                    log.Printf("could not store message: %v", err)
                    http.Error(w, "could not store 
                    message", 
                    http.StatusInternalServerError)
                    return
                }
            }

            // get the current messages and display them
            fmt.Fprintln(w, "Messages:")
            messages, err := c.queryMessages(ctx, 10)
            if err != nil {
                log.Printf("could not get messages: %v", err)
                http.Error(w, "could not get messages", 
                http.StatusInternalServerError)
                return
            }

            for _, message := range messages {
                fmt.Fprintln(w, message.Message)
            }
        }
  1. 创建一个名为main.go的文件,其内容如下:
        package main

        import (
            "log"
            "net/http"
            "os"

            "cloud.google.com/go/datastore"
            "golang.org/x/net/context"
            "google.golang.org/appengine"
        )

        func main() {
            ctx := context.Background()
            log.SetOutput(os.Stderr)

            // Set this in app.yaml when running in production.
            projectID := os.Getenv("GCLOUD_DATASET_ID")

            datastoreClient, err := datastore.NewClient(ctx, projectID)
            if err != nil {
                log.Fatal(err)
            }

            c := Controller{datastoreClient}

            http.HandleFunc("/", c.handle)

            port := os.Getenv("PORT")
            if port == "" {
                port = "8080"
                log.Printf("Defaulting to port %s", port)
            }

            log.Printf("Listening on port %s", port)
            log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
        }
  1. 运行gcloud config set project go-cookbook命令,其中go-cookbook是您在准备部分创建的项目。
  2. 运行gcloud auth application-default login命令并按照说明操作。
  3. 运行export PORT=8080命令。
  4. 运行export GCLOUD_DATASET_ID=go-cookbook命令,其中go-cookbook是您在准备部分创建的项目。
  5. 运行go build命令。
  6. 运行./appengine命令。
  7. 导航至http://localhost:8080/?message=hello%20there
  8. 尝试更多的消息(?message=other
  9. (可选)使用gcloud app deploy将应用部署到您的实例。
  10. 使用gcloud app browse导航到已部署的应用。
  11. 可以选择在以下 URL 清理您的appengine实例和数据存储: * https://console.cloud.google.com/datastore * https://console.cloud.google.com/appengine
  12. go.mod文件可能会被更新,go.sum文件现在应该存在于顶级配方目录中。
  13. 如果您复制或编写了自己的测试,请运行go test命令。确保所有测试都通过。

它是如何工作的。。。

一旦云 SDK 配置为指向您的应用并经过身份验证,GCloud 工具就可以快速部署和配置,允许本地应用访问 Google 服务。

在验证和设置端口后,我们在localhost上运行应用,我们可以开始使用代码。应用定义了一个可以从数据存储中存储和检索的消息对象。这演示了如何隔离这类代码。您还可以使用存储/数据库接口,如前几章所示。

接下来,我们设置一个处理程序,尝试将消息插入数据存储,然后检索所有消息,并在浏览器中显示它们。这创建了一个类似于基本留言簿的东西。您可能会注意到消息并不总是立即出现。如果在没有消息参数的情况下导航或发送另一条消息,则该消息应显示在重新加载页面上。

最后,如果不再使用实例,请确保清理这些实例。

使用 Firebase.google.com/go 与 Firebase 合作

Firebase 是谷歌的另一项云服务,它创建了一个可扩展、易于管理的数据库,可以支持身份验证,尤其适用于移动应用。对于这个配方,我们将使用最新的 Firestore 作为我们的数据库后端。Firebase 服务提供的功能远远超过了本配方中所涵盖的功能,但我们将只关注数据的存储和检索。我们还将研究如何为您的应用设置身份验证,并使用我们自己的自定义客户端包装 Firebase 客户端。

准备

根据以下步骤配置您的环境:

  1. 下载 Go 1.11.1 或更高版本并在您的操作系统上安装 https://golang.org/doc/install
  2. 创建 Firebase 帐户、项目和数据库 https://console.firebase.google.com/

This recipe runs in test mode, which is not secure by default.

  1. 通过转到生成服务管理员令牌 https://console.firebase.google.com/project/go-cookbook/settings/serviceaccounts/adminsdk 。此处,go-cookbook替换为您的项目名称。

  2. 将下载的令牌移动到/tmp/service_account.json

  3. 打开终端或控制台应用,创建并导航到一个项目目录,如~/projects/go-programming-cookbook。我们将在本配方中介绍的所有代码都将在此目录中运行和修改。

  4. 将最新代码克隆到~/projects/go-programming-cookbook-original。在这里,您可以选择从该目录工作,而不是手动键入示例:

$ git clone git@github.com:PacktPublishing/Go-Programming-Cookbook-Second-Edition.git go-programming-cookbook-original

怎么做。。。

这些步骤包括编写和运行应用:

  1. 从终端或控制台应用中,创建一个名为~/projects/go-programming-cookbook/chapter13/firebase的新目录并导航到它。
  2. 运行以下命令:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/firebase 

您应该会看到一个名为go.mod的文件,其中包含以下内容:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/firebase
  1. 创建一个名为client.go的文件,其内容如下:
package firebase

import (
  "context"

  "cloud.google.com/go/firestore"
  "github.com/pkg/errors"
)

// Client Interface for mocking
type Client interface {
  Get(ctx context.Context, key string) (interface{}, error)
  Set(ctx context.Context, key string, value interface{}) error
  Close() error
}

// firestore.Client implements Close()
// we create Get and Set
type firebaseClient struct {
  *firestore.Client
  collection string
}

func (f *firebaseClient) Get(ctx context.Context, key string) (interface{}, error) {
  data, err := f.Collection(f.collection).Doc(key).Get(ctx)
  if err != nil {
    return nil, errors.Wrap(err, "get failed")
  }
  return data.Data(), nil
}

func (f *firebaseClient) Set(ctx context.Context, key string, value interface{}) error {
  set := make(map[string]interface{})
  set[key] = value
  _, err := f.Collection(f.collection).Doc(key).Set(ctx, set)
  return errors.Wrap(err, "set failed")
}
  1. 创建一个名为auth.go的文件,其内容如下:
package firebase

import (
  "context"

  firebase "firebase.google.com/go"
  "github.com/pkg/errors"
  "google.golang.org/api/option"
)

// Authenticate grabs oauth scopes using a generated
// service_account.json file from
// https://console.firebase.google.com/project/go-cookbook/settings/serviceaccounts/adminsdk
func Authenticate(ctx context.Context, collection string) (Client, error) {

  opt := option.WithCredentialsFile("/tmp/service_account.json")
  app, err := firebase.NewApp(ctx, nil, opt)
  if err != nil {
    return nil, errors.Wrap(err, "error initializing app")
  }

  client, err := app.Firestore(ctx)
  if err != nil {
    return nil, errors.Wrap(err, "failed to intialize filestore")
  }
  return &firebaseClient{Client: client, collection: collection}, nil
}
  1. 创建一个名为example的新目录并导航到它。
  2. 创建一个名为main.go的文件,其内容如下:
package main

import (
  "context"
  "fmt"
  "log"

  "github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter13/firebase"
)

func main() {
  ctx := context.Background()
  c, err := firebase.Authenticate(ctx, "collection")
  if err != nil {
    log.Fatalf("error initializing client: %v", err)
  }
  defer c.Close()

  if err := c.Set(ctx, "key", []string{"val1", "val2"}); err != nil {
    log.Fatalf(err.Error())
  }

  res, err := c.Get(ctx, "key")
  if err != nil {
    log.Fatalf(err.Error())
  }
  fmt.Println(res)

  if err := c.Set(ctx, "key2", []string{"val3", "val4"}); err != nil {
    log.Fatalf(err.Error())
  }

  res, err = c.Get(ctx, "key2")
  if err != nil {
    log.Fatalf(err.Error())
  }
  fmt.Println(res)
}
  1. 运行go run main.go
  2. 您也可以运行go build ./example。您应该看到以下输出:
$ go run main.go 
[val1 val2]
[val3 val4]
  1. go.mod文件可能会被更新,go.sum文件现在应该存在于顶级配方目录中。
  2. 如果您复制或编写了自己的测试,请转到一个目录并运行go test。确保所有测试都通过。

它是如何工作的。。。

Firebase 提供了方便的功能,因此您可以使用凭据文件登录。登录后,我们可以存储任何类型的结构化、类似地图的对象。在本例中,我们存储的是map[string]interface{}。许多客户端都可以访问这些数据,包括通过 web 和移动设备。

客户端代码将所有操作包装在一个接口中,以便于测试。在编写客户机代码时,这是一种常见的模式,在其他方法中也有使用。在我们的例子中,我们创建了一个GetSet函数,通过一个键存储和检索一个值。我们还公开了Close(),以便使用客户端的代码可以推迟close(),并在最后清理我们的连接。