Utilizzo di Cloud Spanner con Cloud Functions (2nd gen)

Cloud Spanner

Obiettivi

scrivi, esegui il deployment e attiva funzione Cloud Functions Functions HTTP che accede Spanner.

Costi

Questo documento utilizza Spanner e Cloud Functions, che sono componenti fatturabili di Google Cloud.

  • Per informazioni sul costo di utilizzo di Spanner, consulta Prezzi di Spanner.

  • Per informazioni sul costo dell'utilizzo di Cloud Functions, incluso il servizio di Cloud Functions, consulta i prezzi di Cloud Functions.

Prima di iniziare

  1. Questo documento presuppone che tu abbia un'istanza Spanner denominata test-instance e un database denominato example-db che utilizza la musica applicazione schema. Per istruzioni sulla creazione di un'istanza e un database con l'applicazione Music schema, consulta la Guida rapida all'utilizzo della console oppure i tutorial per iniziare a usare Go, Java, Node.js oppure Python.

  2. Abilitare le API Cloud Functions e Cloud Build.

    Abilita le API

  3. Installa e inizializza gcloud CLI.

    Se hai già installato gcloud CLI, aggiornalo eseguendo il seguente comando:

    gcloud components update
    
  4. Prepara l'ambiente di sviluppo:

prepara l'applicazione

  1. Clona il repository dell'app di esempio nella tua macchina locale:

    Node.js

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

    In alternativa, puoi scarica l'esempio come file ZIP ed estrarlo.

    Python

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

    In alternativa, puoi scarica l'esempio come file ZIP ed estrarlo.

    Vai

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

    In alternativa, puoi scarica l'esempio come file ZIP ed estrarlo.

    Java

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

    In alternativa, puoi scarica l'esempio come file ZIP ed estrarlo.

  2. Passa alla directory che contiene l'esempio di Cloud Functions per accedere a Spanner:

    Node.js

    cd nodejs-docs-samples/functions/spanner/

    Python

    cd python-docs-samples/functions/spanner/

    Vai

    cd golang-samples/functions/spanner/

    Java

    cd java-docs-samples/functions/spanner/
  3. Dai un'occhiata al codice campione:

    Node.js

    // Imports the Google Cloud client library
    const {Spanner} = require('@google-cloud/spanner');
    
    // Imports the functions framework to register your HTTP function
    const functions = require('@google-cloud/functions-framework');
    
    // 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.
     */
    functions.http('spannerQuickstart', 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

    import functions_framework
    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)
    
    
    @functions_framework.http
    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)
    
    

    Vai

    
    // 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"
    	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
    	"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"
    
    func init() {
    	functions.HTTP("HelloSpanner", HelloSpanner)
    }
    
    // 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)
    	}
    }
    

    Java

    import com.google.api.client.http.HttpStatusCodes;
    import com.google.cloud.functions.HttpFunction;
    import com.google.cloud.functions.HttpRequest;
    import com.google.cloud.functions.HttpResponse;
    import com.google.cloud.spanner.DatabaseClient;
    import com.google.cloud.spanner.DatabaseId;
    import com.google.cloud.spanner.LazySpannerInitializer;
    import com.google.cloud.spanner.ResultSet;
    import com.google.cloud.spanner.SpannerException;
    import com.google.cloud.spanner.SpannerOptions;
    import com.google.cloud.spanner.Statement;
    import com.google.common.annotations.VisibleForTesting;
    import com.google.common.base.MoreObjects;
    import java.io.PrintWriter;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    // HelloSpanner is an example of querying Spanner from a Cloud Function.
    public class HelloSpanner implements HttpFunction {
      private static final Logger logger = Logger.getLogger(HelloSpanner.class.getName());
    
      // TODO<developer>: Set these environment variables.
      private static final String SPANNER_INSTANCE_ID =
          MoreObjects.firstNonNull(System.getenv("SPANNER_INSTANCE"), "my-instance");
      private static final String SPANNER_DATABASE_ID =
          MoreObjects.firstNonNull(System.getenv("SPANNER_DATABASE"), "example-db");
    
      private static final DatabaseId databaseId =
          DatabaseId.of(
              SpannerOptions.getDefaultProjectId(),
              SPANNER_INSTANCE_ID,
              SPANNER_DATABASE_ID);
    
      // The LazySpannerInitializer instance is shared across all instances of the HelloSpanner class.
      // It will create a Spanner instance the first time one is requested, and continue to return that
      // instance for all subsequent requests.
      private static final LazySpannerInitializer SPANNER_INITIALIZER = new LazySpannerInitializer();
    
      @VisibleForTesting
      DatabaseClient getClient() throws Throwable {
        return SPANNER_INITIALIZER.get().getDatabaseClient(databaseId);
      }
    
      @Override
      public void service(HttpRequest request, HttpResponse response) throws Exception {
        var writer = new PrintWriter(response.getWriter());
        try {
          DatabaseClient client = getClient();
          try (ResultSet rs =
              client
                  .singleUse()
                  .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) {
            writer.printf("Albums:%n");
            while (rs.next()) {
              writer.printf(
                  "%d %d %s%n",
                  rs.getLong("SingerId"), rs.getLong("AlbumId"), rs.getString("AlbumTitle"));
            }
          } catch (SpannerException e) {
            writer.printf("Error querying database: %s%n", e.getMessage());
            response.setStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR, e.getMessage());
          }
        } catch (Throwable t) {
          logger.log(Level.SEVERE, "Spanner example failed", t);
          writer.printf("Error setting up Spanner: %s%n", t.getMessage());
          response.setStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR, t.getMessage());
        }
      }
    }

    La funzione invia una query SQL per recuperare tutti i dati Albums dal tuo per configurare un database. La funzione viene eseguita quando effettui una richiesta HTTP all'endpoint della funzione.

esegui il deployment della funzione

Per eseguire il deployment della funzione con un trigger HTTP, esegui questo comando nella directory spanner:

Node.js

gcloud functions deploy nodejs-spanner-function \
--gen2 \
--runtime=nodejs20 \
--region=REGION \
--source=. \
--entry-point=spannerQuickstart 
--trigger-http

Utilizza la --runtime per specificare l'ID runtime di un versione Node.js supportata per l'esecuzione la tua funzione.

Python

gcloud functions deploy python-spanner-function \
--gen2 \
--runtime=python312 \
--region=REGION \
--source=. \
--entry-point=spanner_read_data 
--trigger-http

Utilizza la --runtime per specificare l'ID runtime di un versione Python supportata per l'esecuzione la tua funzione.

Vai

gcloud functions deploy go-spanner-function \
--gen2 \
--runtime=go121 \
--region=REGION \
--source=. \
--entry-point=HelloSpanner 
--trigger-http

Utilizza la --runtime per specificare l'ID runtime di un versione Go supportata per l'esecuzione la tua funzione.

Java

gcloud functions deploy java-spanner-function \
--gen2 \
--runtime=java17 \
--region=REGION \
--source=. \
--entry-point=functions.HelloSpanner \
--memory=512MB 
--trigger-http

Utilizza la --runtime per specificare l'ID runtime di un versione Java supportata per l'esecuzione la tua funzione.

Sostituisci REGION con il nome della regione Google Cloud in cui vuoi eseguire il deployment della funzione (ad es. us-west1).

Il deployment delle funzioni potrebbe richiedere fino a due minuti.

Osserva il valore url restituito al termine del deployment della funzione. Potrai da utilizzare quando attivi la funzione.

Puoi visualizzare le funzioni di cui hai eseguito il deployment nella nella pagina Cloud Functions nella console Google Cloud. Puoi anche creare e modificare le funzioni in quella pagina e ottenere i dettagli e la diagnostica delle tue funzioni.

Attiva la funzione

Effettua una richiesta HTTP alla tua funzione:

curl URL

Sostituisci URL con il valore dell'URL restituito quando la funzione del deployment.

Dovresti vedere un output che mostra i risultati Query SQL, supponendo che tu abbia seguito un corso introduttivo tutorial e ha compilato il database:

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

Puoi anche visitare l'URL della funzione nel tuo browser per vedere il risultato di una query SQL.

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi aggiuntivi per Risorse Spanner e Cloud Functions utilizzate in questo documento:

  1. Elimina l'istanza:

    gcloud spanner instances delete test-instance
    
  2. Elimina la funzione di cui hai eseguito il deployment:

    Node.js

    gcloud functions delete nodejs-spanner-function 

    Python

    gcloud functions delete python-spanner-function 

    Vai

    gcloud functions delete go-spanner-function 

    Java

    gcloud functions delete java-spanner-function 

Passaggi successivi