Usa Cloud Spanner con Cloud Functions

Cloud Spanner

Objetivos

Escribe, implementa y activa una Cloud Function de HTTP que tenga acceso a Spanner.

Costos

Este tema utiliza Spanner y Cloud Functions, que son componentes facturables de Google Cloud.

  • Para obtener información sobre el costo de usar Spanner, consulta Precios de Spanner.

  • Para obtener información sobre el costo del uso de Cloud Functions, incluidas las invocaciones gratuitas, consulta Precios de Cloud Functions.

Antes de comenzar

  1. En este tema, se supone que tienes una instancia de Spanner llamada test-instance y una base de datos llamada example-db que usa el esquema de la aplicación de música. A fin de obtener instrucciones para crear una instancia y una base de datos con el esquema de aplicación de música, consulta la guía de inicio rápido sobre el uso de Console o los instructivos sobre introducción a Go, Java, Node.js o Python.

  2. Habilita la API de Cloud Functions.

    Habilitar la API

  3. Instala e inicializa el SDK de Cloud.

  4. Actualiza los componentes de gcloud:

    gcloud components update
        
  5. Prepara tu entorno de desarrollo:

Prepara la aplicación

  1. Clona el repositorio de la app de muestra en tu máquina local:

    Node.js

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

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

    Python

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

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

    Go

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

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

  2. Cambia al directorio que contiene el código de muestra de Cloud Functions para acceder a Spanner.

    Node.js

    cd nodejs-docs-samples/functions/spanner/

    Python

    cd python-docs-samples/functions/spanner/

    Go

    cd golang-samples/functions/spanner/
  3. Observa el código de muestra:

    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)
        	}
        }
        

    La función envía una consulta de SQL para recuperar todos los datos de Albums de tu base de datos. La función se ejecuta cuando haces una solicitud HTTP al extremo de la función.

Implementa la función

Ejecuta el siguiente comando en el directorio spanner para implementar la función con un activador HTTP:

Node.js

gcloud functions deploy get --runtime nodejs8 --trigger-http
Puedes usar los siguientes valores para que la marca --runtime especifique tu versión preferida de 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
Puedes usar los siguientes valores para que la marca --runtime especifique tu versión preferida de Go:
  • go111
  • go113 (Beta)

La implementación de la función puede tardar hasta 2 minutos.

Ten en cuenta el valor de url que se muestra cuando tu función termina de implementarse. Lo usarás cuando actives la función.

Puedes ver las funciones implementadas en la página de Cloud Functions en Google Cloud Console. También puedes crear y editar funciones en esa página, en la que además puedes obtener detalles y diagnósticos de tus funciones.

Activa la función

Haz una solicitud HTTP a tu función:

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

donde REGION y PROJECT_ID coinciden con los valores que son visibles en la terminal cuando la función se termina de implementar. Deberías ver una respuesta que muestre los resultados de la consulta de SQL, si es que trabajaste con un instructivo de introducción y propagaste la base de datos:

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
    

También puedes visitar la URL de la función en tu navegador para ver los resultados de tu consulta de SQL.

Limpieza

Para evitar incurrir en cargos adicionales a tu cuenta de Google Cloud por los recursos de Spanner y Cloud Functions utilizados en este tema, haz lo siguiente:

  1. Borra la instancia:

    gcloud spanner instances delete test-instance
        
  2. Borra la función que implementaste:

    Node.js

    gcloud functions delete get --trigger-http

    Python

    gcloud functions delete spanner_read_data --trigger-http

    Go

    gcloud functions delete HelloSpanner --trigger-http

Qué sigue