Como usar o Cloud Spanner com o Cloud Functions

Cloud Spanner

Objetivos

Grave, implante e acione uma função do Cloud HTTP que acesse o Spanner.

Custos

Este tópico usa o Spanner e o Cloud Functions, que são componentes faturáveis do Google Cloud.

  • Para mais informações sobre o custo do uso do Spanner, consulte os preços do Spanner.

  • Para informações sobre o custo do uso do Cloud Functions, incluindo invocações gratuitas, consulte os preços do Cloud Functions.

Antes de começar

  1. Neste tópico, pressupomos que você tem uma instância do Spanner chamada test-instance e um banco de dados denominado example-db que usa o esquema do aplicativo de música. Para ver instruções sobre como criar uma instância e um banco de dados com o esquema do aplicativo de música, consulte o Guia de início rápido do uso do console ou os tutoriais de primeiros passos em Go, Java, Node.js ou Python.

  2. Ative a API Cloud Functions.

    Ative a API

  3. Instale e inicialize o SDK do Cloud.

  4. Atualize os componentes gcloud:

    gcloud components update
        
  5. Prepare seu ambiente de desenvolvimento:

Preparar o aplicativo

  1. Clone o repositório do app de amostra na máquina local:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Outra opção é fazer o download da amostra como um arquivo ZIP e extraí-la.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Outra opção é fazer o download da amostra como um arquivo ZIP e extraí-la.

    Go

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    Outra opção é fazer o download da amostra como um arquivo ZIP e extraí-la.

  2. Altere para o diretório que contém o código de amostra do Cloud Functions para acessar o Spanner:

    Node.js

    cd nodejs-docs-samples/functions/spanner/

    Python

    cd python-docs-samples/functions/spanner/

    Go

    cd golang-samples/functions/spanner/
  3. Confira o código de amostra:

    Node.js

    // Imports the Google Cloud client library
        const {Spanner} = require('@google-cloud/spanner');
    
        // Instantiates a client
        const spanner = new Spanner();
    
        // Your Cloud Spanner instance ID
        const instanceId = 'test-instance';
    
        // Your Cloud Spanner database ID
        const databaseId = 'example-db';
    
        /**
         * HTTP Cloud Function.
         *
         * @param {Object} req Cloud Function request context.
         * @param {Object} res Cloud Function response context.
         */
        exports.get = async (req, res) => {
          // Gets a reference to a Cloud Spanner instance and database
          const instance = spanner.instance(instanceId);
          const database = instance.database(databaseId);
    
          // The query to execute
          const query = {
            sql: 'SELECT * FROM Albums',
          };
    
          // Execute the query
          try {
            const results = await database.run(query);
            const rows = results[0].map(row => row.toJSON());
            rows.forEach(row => {
              res.write(
                `SingerId: ${row.SingerId}, ` +
                  `AlbumId: ${row.AlbumId}, ` +
                  `AlbumTitle: ${row.AlbumTitle}\n`
              );
            });
            res.status(200).end();
          } catch (err) {
            res.status(500).send(`Error querying Spanner: ${err}`);
          }
        };

    Python

    from google.cloud import spanner
    
        instance_id = 'test-instance'
        database_id = 'example-db'
    
        client = spanner.Client()
        instance = client.instance(instance_id)
        database = instance.database(database_id)
    
        def spanner_read_data(request):
            query = 'SELECT * FROM Albums'
    
            outputs = []
            with database.snapshot() as snapshot:
                results = snapshot.execute_sql(query)
    
                for row in results:
                    output = 'SingerId: {}, AlbumId: {}, AlbumTitle: {}'.format(*row)
                    outputs.append(output)
    
            return '\n'.join(outputs)

    Go

    
        // Package spanner contains an example of using Spanner from a Cloud Function.
        package spanner
    
        import (
        	"context"
        	"fmt"
        	"log"
        	"net/http"
        	"sync"
    
        	"cloud.google.com/go/spanner"
        	"google.golang.org/api/iterator"
        )
    
        // client is a global Spanner client, to avoid initializing a new client for
        // every request.
        var client *spanner.Client
        var clientOnce sync.Once
    
        // db is the name of the database to query.
        var db = "projects/my-project/instances/my-instance/databases/example-db"
    
        // HelloSpanner is an example of querying Spanner from a Cloud Function.
        func HelloSpanner(w http.ResponseWriter, r *http.Request) {
        	clientOnce.Do(func() {
        		// Declare a separate err variable to avoid shadowing client.
        		var err error
        		client, err = spanner.NewClient(context.Background(), db)
        		if err != nil {
        			http.Error(w, "Error initializing database", http.StatusInternalServerError)
        			log.Printf("spanner.NewClient: %v", err)
        			return
        		}
        	})
    
        	fmt.Fprintln(w, "Albums:")
        	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
        	iter := client.Single().Query(r.Context(), stmt)
        	defer iter.Stop()
        	for {
        		row, err := iter.Next()
        		if err == iterator.Done {
        			return
        		}
        		if err != nil {
        			http.Error(w, "Error querying database", http.StatusInternalServerError)
        			log.Printf("iter.Next: %v", err)
        			return
        		}
        		var singerID, albumID int64
        		var albumTitle string
        		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
        			http.Error(w, "Error parsing database response", http.StatusInternalServerError)
        			log.Printf("row.Columns: %v", err)
        			return
        		}
        		fmt.Fprintf(w, "%d %d %s\n", singerID, albumID, albumTitle)
        	}
        }
        

    A função envia uma consulta SQL para buscar todos os dados de Albums do banco de dados. A função é executada quando você faz uma solicitação HTTP ao endpoint da função.

Implantar a função

Para implantar a função com um gatilho HTTP, execute o seguinte comando no diretório spanner:

Node.js

gcloud functions deploy get --runtime nodejs8 --trigger-http
Use estes valores da sinalização --runtime para especificar sua versão preferencial do Node.js:
  • nodejs6 (obsoleto)
  • nodejs8
  • nodejs10 (beta)

Python

gcloud functions deploy spanner_read_data --runtime python37 --trigger-http

Go

gcloud functions deploy HelloSpanner --runtime go111 --trigger-http
É possível usar os seguintes valores para a sinalização --runtime para especificar sua versão preferencial do Go:
  • go111
  • go113 (beta)

A implantação da função pode levar até 2 minutos.

Observe o valor de url retornado quando a implantação da função for concluída. Você vai usá-lo ao acionar a função.

É possível ver as funções implementadas na página do Cloud Functions no Console do Google Cloud. Nessa página, também é possível criar e editar funções, assim como obter detalhes e diagnósticos das suas funções.

Acionar a função

Faça uma solicitação HTTP à função:

Node.js

curl "https://REGION-PROJECT_ID.cloudfunctions.net/get" --trigger-http

Python

curl "https://REGION-PROJECT_ID.cloudfunctions.net/spanner_read_data" --trigger-http

Go

curl "https://REGION-PROJECT_ID.cloudfunctions.net/HelloSpanner" --trigger-http

em que REGION e PROJECT_ID correspondem aos valores visíveis no terminal quando a implantação da função for concluída. Você verá os resultados da consulta SQL, supondo que você seguiu um tutorial de primeiros passos e preencheu o banco de dados:

SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold Your Peace
    SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
    SingerId: 2, AlbumId: 1, AlbumTitle: Green
    SingerId: 2, AlbumId: 3, AlbumTitle: Terrified
    SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
    

Você também pode visitar o URL da função no seu navegador para ver o resultado da sua consulta SQL.

Fazer a limpeza

Para evitar cobranças adicionais em sua conta do Google Cloud pelos recursos do Spanner e do Cloud Functions usados neste tópico:

  1. Exclua a instância:

    gcloud spanner instances delete test-instance
        
  2. Exclua a função que você implantou:

    Node.js

    gcloud functions delete get --trigger-http

    Python

    gcloud functions delete spanner_read_data --trigger-http

    Go

    gcloud functions delete HelloSpanner --trigger-http

A seguir