Configurazione delle richieste di riscaldamento per migliorare le prestazioni

Puoi utilizzare le richieste di riscaldamento per ridurre la latenza di richieste e risposte durante il caricamento del codice dell'app in un'istanza appena creata.

App Engine ha spesso bisogno di caricare il codice dell'app in una nuova istanza. Il caricamento di un'istanza può verificarsi nelle seguenti situazioni:

  • Quando esegui nuovamente il deployment di una versione dell'app.
  • Quando vengono create nuove istanze a causa del carico proveniente dalle richieste che supera la capacità dell'insieme attuale di istanze in esecuzione.
  • Quando avvengono le operazioni di manutenzione e riparazione dell'infrastruttura o dell'hardware fisico di base.

Il caricamento del codice della tua app in una nuova istanza può comportare il caricamento delle richieste. Le richieste di caricamento possono comportare una maggiore latenza delle richieste per i tuoi utenti, ma puoi evitare questa latenza utilizzando le richieste di preparazione. Le richieste di riscaldamento caricano il codice dell'app in una nuova istanza prima che qualsiasi richiesta in tempo reale raggiunga quell'istanza.

Se per la tua applicazione sono abilitate le richieste di riscaldamento, App Engine tenta di rilevare quando l'applicazione ha bisogno di una nuova istanza e avvia una richiesta di riscaldamento per inizializzare una nuova istanza. Tuttavia, questi tentativi di rilevamento non funzionano in tutti i casi. Di conseguenza, potresti riscontrare richieste di caricamento, anche se nella tua app sono abilitate le richieste di preparazione. Ad esempio, se la tua app non gestisce traffico, la prima richiesta all'app sarà sempre una richiesta di caricamento, non una richiesta di warmup.

Le richieste di riscaldamento utilizzano le ore di istanza come qualsiasi altra richiesta all'applicazione App Engine. Nella maggior parte dei casi in cui le richieste di riscaldamento sono abilitate, non noterai un aumento delle ore di istanza perché l'applicazione viene semplicemente inizializzata in una richiesta di warmup anziché in una richiesta di caricamento. L'utilizzo delle ore di istanza può aumentare se decidi di svolgere più attività, ad esempio la pre-memorizzazione nella cache durante una richiesta di warmup. Se imposti min_idle_instances su un valore superiore a 0, potresti ricevere richieste di riscaldamento al primo avvio di queste istanze, ma rimarranno disponibili dopo questo periodo.

Attivazione delle richieste di riscaldamento in corso...

Le richieste di riscaldamento vengono utilizzate dallo scheduler di App Engine, che controlla la scalabilità automatica delle istanze in base alla configurazione fornita dall'utente. Con le richieste di riscaldamento abilitate, App Engine invia richieste GET a /_ah/warmup. Puoi implementare gestori per questa richiesta per eseguire attività specifiche per l'applicazione, ad esempio la memorizzazione preliminare nella cache dei dati dell'applicazione.

Lo scheduler avvia le istanze quando determina che sono necessarie più istanze. Le richieste di riscaldamento potrebbero essere visualizzate nei log anche se sono disabilitate perché lo scheduler le utilizza per avviare le istanze.

Tieni presente che non è garantito che le richieste di riscaldamento vengano richiamate. In alcune situazioni vengono inviate invece le richieste di caricamento, ad esempio se l'istanza è la prima a essere avviata o se il traffico è in rapida accelerazione. Tuttavia, ci sarà un tentativo "best effort" di inviare richieste a istanze già sottoposte a riscaldamento se sono abilitate.

Per abilitare le richieste di riscaldamento, aggiungi l'elemento warmup sotto la direttiva inbound_services nel file app.yaml, ad esempio:

inbound_services:
- warmup

Creazione del gestore

Crea un gestore che elaborerà le richieste inviate a /_ah/warmup. Il gestore dovrebbe eseguire qualsiasi logica di riscaldamento necessaria per la tua app.


// Sample warmup demonstrates usage of the /_ah/warmup handler.
package main

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

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

var startupTime time.Time
var client *storage.Client

func main() {
	// Perform required setup steps for the application to function.
	// This assumes any returned error requires a new instance to be created.
	if err := setup(context.Background()); err != nil {
		log.Fatalf("setup: %v", err)
	}

	// Log when an appengine warmup request is used to create the new instance.
	// Warmup steps are taken in setup for consistency with "cold start" instances.
	http.HandleFunc("/_ah/warmup", func(w http.ResponseWriter, r *http.Request) {
		log.Println("warmup done")
	})
	http.HandleFunc("/", indexHandler)

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

	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

// setup executes per-instance one-time warmup and initialization actions.
func setup(ctx context.Context) error {
	// Store the startup time of the server.
	startupTime = time.Now()

	// Initialize a Google Cloud Storage client.
	var err error
	if client, err = storage.NewClient(ctx); err != nil {
		return err
	}

	return nil
}

// indexHandler responds to requests with our greeting.
func indexHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}
	uptime := time.Since(startupTime).Seconds()
	fmt.Fprintf(w, "Hello, World! Uptime: %.2fs\n", uptime)
}