Gestione delle sessioni con Firestore

Questo tutorial mostra come gestire le sessioni su Cloud Run.

Molte app richiedono la gestione delle sessioni per l'autenticazione e le preferenze utente. Il pacchetto Gorilla Web Toolkit sessions include un'implementazione basata sul file system per eseguire questa funzione. Tuttavia, questa implementazione non è adatta a un'app che può essere pubblicata da più istanze, perché la sessione registrata in un'istanza potrebbe differire da altre istanze. Il pacchetto gorilla/sessions include anche un'implementazione basata sui cookie. Tuttavia, questa implementazione richiede la crittografia dei cookie e l'archiviazione dell'intera sessione sul client, anziché solo di un ID sessione, che potrebbe essere troppo grande per alcune app.

Obiettivi

  • Scrivi l'app.
  • Esegui l'app localmente.
  • Esegui il deployment dell'app su Cloud Run.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il calcolatore prezzi.

I nuovi utenti di Google Cloud potrebbero avere diritto a una prova senza costi.

Al termine delle attività descritte in questo documento, puoi evitare l'addebito di ulteriori costi eliminando le risorse che hai creato. Per ulteriori informazioni, vedi Pulizia.

Prima di iniziare

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the Firestore API.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  6. Verify that billing is enabled for your Google Cloud project.

  7. Enable the Firestore API.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

  8. Nella console Google Cloud , apri l'app in Cloud Shell.

    Vai a Cloud Shell

    Cloud Shell fornisce l'accesso da riga di comando alle risorse cloud direttamente dal browser. Apri Cloud Shell nel browser e fai clic su Continua per scaricare il codice campione e modificare la directory dell'app.

  9. In Cloud Shell, configura gcloud CLI in modo da utilizzare il nuovo progetto Google Cloud :
    # Configure gcloud for your project
    gcloud config set project YOUR_PROJECT_ID
    

Configurazione del progetto

  1. Nella finestra del terminale, clona il repository dell'app di esempio sulla tua macchina locale:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
  2. Passa alla directory che contiene il codice di esempio:

    cd golang-samples/getting-started/sessions

Informazioni sull'app web

Questa app mostra saluti in lingue diverse per ogni utente. Gli utenti di ritorno vengono sempre accolti nella stessa lingua.

Più finestre dell'app che mostrano un saluto in lingue diverse.

Prima che l'app possa memorizzare le preferenze di un utente, devi disporre di un modo per memorizzare le informazioni sull'utente corrente in una sessione. Questa app di esempio utilizza Firestore per archiviare i dati delle sessioni.

  1. L'app inizia importando le dipendenze, definendo un tipo app per contenere un sessions.Store e un modello HTML e definendo l'elenco dei saluti.

    import (
    	"context"
    	"html/template"
    	"log"
    	"math/rand"
    	"net/http"
    	"os"
    
    	"cloud.google.com/go/firestore"
    )
    
    // app stores a sessions.Store. Create a new app with newApp.
    type app struct {
    	tmpl         *template.Template
    	collectionID string
    	projectID    string
    }
    
    // session stores the client's session information.
    // This type is also used for executing the template.
    type session struct {
    	Greetings string `json:"greeting"`
    	Views     int    `json:"views"`
    }
    
    // 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. Successivamente, l'app definisce una funzione main, che crea una nuova istanza app, registra il gestore dell'indice e avvia il server HTTP. La funzione newApp crea l'istanza app impostando i valori projectID e collectionID e analizza il modello HTML.

    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")
    	}
    
    	// collectionID is a non-empty identifier for this app, it is used as the Firestore
    	// collection name that stores the sessions.
    	//
    	// Set it to something more descriptive for your app.
    	collectionID := "hello-views"
    
    	a, err := newApp(projectID, collectionID)
    	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, collectionID string) (app, error) {
    	tmpl, err := template.New("Index").Parse(`<body>{{.Views}} {{if eq .Views 1}}view{{else}}views{{end}} for "{{.Greetings}}"</body>`)
    	if err != nil {
    		log.Fatalf("template.New: %v", err)
    	}
    
    	return app{
    		tmpl:         tmpl,
    		collectionID: collectionID,
    		projectID:    projectID,
    	}, nil
    }
    
  3. Il gestore dell'indice recupera la sessione dell'utente, creandone una se necessario. Alle nuove sessioni vengono assegnati una lingua casuale e un numero di visualizzazioni pari a 0. Il conteggio delle visualizzazioni viene quindi aumentato di uno, la sessione viene salvata e il modello HTML scrive la risposta.

    
    // index uses sessions to assign users a random greeting and keep track of
    // views.
    func (a *app) index(w http.ResponseWriter, r *http.Request) {
    	// Allows requests only for the root path ("/") to prevent duplicate calls.
    	if r.RequestURI != "/" {
    		http.NotFound(w, r)
    		return
    	}
    
    	var session session
    	var doc *firestore.DocumentRef
    
    	isNewSession := false
    
    	ctx := context.Background()
    
    	client, err := firestore.NewClient(ctx, a.projectID)
    	if err != nil {
    		log.Fatalf("firestore.NewClient: %v", err)
    	}
    	defer client.Close()
    
    	// cookieName is a non-empty identifier for this app, it is used as the key name
    	// that contains the session's id value.
    	//
    	// Set it to something more descriptive for your app.
    	cookieName := "session_id"
    
    	// If err is different to nil, it means the cookie has not been set, so it will be created.
    	cookie, err := r.Cookie(cookieName)
    	if err != nil {
    		// isNewSession flag is set to true
    		isNewSession = true
    	}
    
    	// If isNewSession flag is true, the session will be created
    	if isNewSession {
    		// Get unique id for new document
    		doc = client.Collection(a.collectionID).NewDoc()
    
    		session.Greetings = greetings[rand.Intn(len(greetings))]
    		session.Views = 1
    
    		// Cookie is set
    		cookie = &http.Cookie{
    			Name:  cookieName,
    			Value: doc.ID,
    		}
    		http.SetCookie(w, cookie)
    	} else {
    		// The session exists
    
    		// Retrieve document from collection by ID
    		docSnapshot, err := client.Collection(a.collectionID).Doc(cookie.Value).Get(ctx)
    		if err != nil {
    			log.Printf("doc.Get error: %v", err)
    			http.Error(w, "Error getting session", http.StatusInternalServerError)
    			return
    		}
    
    		// Unmarshal documents's content to local type
    		err = docSnapshot.DataTo(&session)
    		if err != nil {
    			log.Printf("doc.DataTo error: %v", err)
    			http.Error(w, "Error parsing session", http.StatusInternalServerError)
    			return
    		}
    
    		doc = docSnapshot.Ref
    
    		// Add 1 to current views value
    		session.Views++
    	}
    
    	// The document is created/updated
    	_, err = doc.Set(ctx, session)
    	if err != nil {
    		log.Printf("doc.Set error: %v", err)
    		http.Error(w, "Error creating session", http.StatusInternalServerError)
    		return
    	}
    
    	if err := a.tmpl.Execute(w, session); err != nil {
    		log.Printf("Execute: %v", err)
    	}
    }
    

    Il seguente diagramma illustra come Firestore gestisce le sessioni per l'app Cloud Run.

    Diagramma dell&#39;architettura: utente, Cloud Run, Firestore.

Eliminazione delle sessioni

Puoi eliminare i dati di sessione nella Google Cloud console o implementare una strategia di eliminazione automatica. Se utilizzi soluzioni di archiviazione per le sessioni come Memcache o Redis, le sessioni scadute vengono eliminate automaticamente.

Esecuzione in locale

  1. Nella finestra del terminale, crea il file binario sessions:

    go build
    
  2. Avvia il server HTTP:

    ./sessions
    
  3. Visualizza l'app nel browser web:

    Cloud Shell

    Nella barra degli strumenti di Cloud Shell, fai clic su Anteprima web Anteprima web e seleziona Anteprima sulla porta 8080.

    Macchina locale

    Nel browser, vai a http://localhost:8080

    Viene visualizzato uno dei cinque saluti: "Hello World", "Hallo Welt", "Hola mundo", "Salut le Monde" o "Ciao Mondo". La lingua cambia se apri la pagina in un browser diverso o in modalità di navigazione in incognito. Puoi visualizzare e modificare i dati della sessione nella consoleGoogle Cloud .

    Sessioni Firestore nella console Google Cloud .

  4. Per arrestare il server HTTP, premi Control+C nella finestra del terminale.

Deployment ed esecuzione su Cloud Run

Puoi utilizzare Cloud Run per creare ed eseguire il deployment di un'app che funziona in modo affidabile anche se sottoposta a un carico elevato e con grandi quantità di dati.

  1. Esegui il deployment dell'app su Cloud Run:
        gcloud run deploy firestore-tutorial-go 
    --source . --allow-unauthenticated --port=8080
    --set-env-vars=GOOGLE_CLOUD_PROJECT=YOUR_PROJECT_ID
  2. Visita l'URL restituito da questo comando per vedere come i dati di sessione vengono mantenuti tra i caricamenti di pagina.

Il saluto viene ora fornito da un server web in esecuzione su un'istanza Cloud Run.

Esecuzione del debug dell'app

Se non riesci a connetterti alla tua app Cloud Run, controlla quanto segue:

  1. Verifica che i comandi di deployment gcloud siano stati completati correttamente e non abbiano generato errori. Se si sono verificati errori (ad esempio, message=Build failed), correggili e prova a eseguire di nuovo il deployment dell'app Cloud Run.
  2. Nella console Google Cloud , vai alla pagina Esplora log.

    Vai alla pagina Esplora log

    1. Nell'elenco a discesa Risorse selezionate di recente, fai clic su Applicazione Cloud Run, quindi fai clic su Tutti i module_id. Viene visualizzato un elenco di richieste relative alle tue visite all'app. Se non vedi un elenco di richieste, verifica di aver selezionato Tutti i module_id dall'elenco a discesa. Se visualizzi messaggi di errore stampati nella console Google Cloud , verifica che il codice dell'app corrisponda al codice nella sezione relativa alla scrittura dell'app web.

    2. Assicurati che l'API Firestore sia abilitata.

Esegui la pulizia

Elimina il progetto

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Elimina l'istanza Cloud Run

  1. In the Google Cloud console, go to the Versions page for App Engine.

    Go to Versions

  2. Select the checkbox for the non-default app version that you want to delete.
  3. Per eliminare la versione dell'app, fai clic su Elimina.

Passaggi successivi