Como configurar solicitações de aquecimento para melhorar o desempenho

Use as solicitações de aquecimento para reduzir a latência da solicitação e da resposta enquanto o código do aplicativo é carregado em uma instância recém-criada.

No App Engine, há uma necessidade constante de carregar o código do aplicativo em uma nova instância. O carregamento de uma instância pode acontecer nas seguintes situações:

  • Quando você reimplanta uma versão do aplicativo.
  • Quando novas instâncias são criadas devido ao carregamento de solicitações que excedem a capacidade do conjunto atual de instâncias em execução.
  • Quando ocorre manutenção e reparo da infraestrutura subjacente ou do hardware físico.

O carregamento do código do aplicativo em uma nova instância pode resultar em solicitações de carregamento. As solicitações de carregamento podem resultar em maior latência das solicitações para seus usuários. Para evitar isso, use as solicitações de aquecimento. Com elas, o código do aplicativo é carregado para uma nova instância antes que as solicitações ativas cheguem a essa instância.

Se as solicitações de aquecimento estiverem ativas no seu aplicativo, ocorrerá uma tentativa de detectar quando o aplicativo precisará de uma nova instância e de uma solicitação de aquecimento para que a inicialização dessa instância seja gerada. No entanto, essas tentativas de detecção não funcionam em todos os casos. Como resultado, é possível que você encontre solicitações de carregamento, mesmo que as solicitações de aquecimento estejam ativadas em seu aplicativo. Por exemplo, se o aplicativo não atender ao tráfego, a primeira solicitação para o aplicativo será sempre de carregamento e não de aquecimento.

Nas solicitações de aquecimento, como em qualquer outra solicitação para seu aplicativo do App Engine, são usadas as instâncias/hora. Na maioria dos casos em que as solicitações de aquecimento estão ativadas, você não notará um aumento na instância/hora porque o aplicativo está apenas sendo inicializado em uma solicitação de aquecimento, e não de carregamento. O uso de instância/hora aumentará se você decidir executar mais operações, como realizar o pré-armazenamento em cache durante uma solicitação de aquecimento. Se você definir min_idle_instances como maior que 0, poderá encontrar solicitações de aquecimento quando essas instâncias forem iniciadas, mas elas permanecerão disponíveis depois desse tempo.

Como ativar solicitações de aquecimento

As solicitações de aquecimento são usadas pelo programador do App Engine, em que é feito o controle do escalonamento automático de instâncias com base na configuração definida pelo usuário. Com as solicitações de aquecimento ativadas, o App Engine envia solicitações GET para /_ah/warmup. É possível implementar gerenciadores nessa solicitação para realizar tarefas específicas do aplicativo, como pré-armazenar dados em cache.

O programador inicia instâncias quando determina que mais instâncias são necessárias. As solicitações de aquecimento podem resultar em registros ainda que estejam desativadas, porque são usadas pelo programador para iniciar instâncias.

Não há garantia de chamada das solicitações de aquecimento. Em algumas situações, solicitações de carregamento são enviadas no lugar delas, por exemplo, se a instância a ser iniciada for a primeira ou se houver um grande aumento no tráfego. No entanto, haverá uma tentativa de enviar solicitações para instâncias já aquecidas se as solicitações de aquecimento estiverem ativadas.

Para ativar solicitações de aquecimento, adicione o elemento warmup à diretiva inbound_services no arquivo app.yaml, por exemplo:

inbound_services:
- warmup

Como criar o gerenciador

Crie um gerenciador que processará as solicitações enviadas para /_ah/warmup. Ele precisa executar qualquer lógica de aquecimento que o aplicativo necessitar.


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