Optimiza las redes (1ª gen.)
La simplicidad de Cloud Run Functions permite programar código rápidamente y ejecutarlo en un entorno sin servidores. A una escala moderada, el costo de ejecutar funciones es bajo, y puedes considerar que optimizar el código no es una prioridad importante. Sin embargo, a medida que tu implementación aumenta su escala, optimizar tu código se vuelve cada vez más relevante.
En este documento, se describe cómo optimizar las redes para tus funciones. Algunos de los beneficios de optimizar las redes son los siguientes:
- Reduce el tiempo de CPU que se dedica a establecer conexiones nuevas en cada llamada a función.
- Reduce la probabilidad de agotar las cuotas de DNS o de conexión.
Mantén conexiones persistentes
En esta sección se muestran ejemplos sobre cómo mantener conexiones persistentes en una función. No hacerlo puede causar que agotes tu cuota de conexión rápidamente.
En esta sección, se abordan los siguientes casos:
- HTTP/S
- API de Google
Solicitudes HTTP(S)
En el siguiente fragmento de código optimizado, se muestra cómo mantener conexiones persistentes en lugar de crear una nueva para cada invocación de función:
Node.js
Python
Go
PHP
Te recomendamos que uses el framework de HTTP para Guzzle PHP para enviar solicitudes HTTP, ya que administra las conexiones persistentes automáticamente.
Accede a las APIs de Google
En el ejemplo siguiente, se usa Cloud Pub/Sub, pero este enfoque también funciona para otras bibliotecas cliente, como Cloud Natural Language o Cloud Spanner. Ten en cuenta que las mejoras de rendimiento pueden depender de la implementación actual de algunas bibliotecas cliente en particular.
Crear un objeto cliente de Pub/Sub genera una conexión y dos consultas de DNS por invocación. Para evitar consultas de DNS y conexiones innecesarias, crea el objeto cliente de Pub/Sub en alcance global, como se indica en el ejemplo siguiente:
Node.js
Python
Go
// Package contexttip is an example of how to use Pub/Sub and context.Context in // a Cloud Function. package contexttip import ( "context" "encoding/json" "fmt" "log" "net/http" "os" "sync" "cloud.google.com/go/pubsub" "github.com/GoogleCloudPlatform/functions-framework-go/functions" ) // client is a global Pub/Sub client, initialized once per instance. var client *pubsub.Client var once sync.Once // createClient creates the global pubsub Client func createClient() { // GOOGLE_CLOUD_PROJECT is a user-set environment variable. var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT") // err is pre-declared to avoid shadowing client. var err error // client is initialized with context.Background() because it should // persist between function invocations. client, err = pubsub.NewClient(context.Background(), projectID) if err != nil { log.Fatalf("pubsub.NewClient: %v", err) } } func init() { // register http function functions.HTTP("PublishMessage", PublishMessage) } type publishRequest struct { Topic stringjson:"topic"Message stringjson:"message"} // PublishMessage publishes a message to Pub/Sub. PublishMessage only works // with topics that already exist. func PublishMessage(w http.ResponseWriter, r *http.Request) { // use of sync.Once ensures client is only created once. once.Do(createClient) // Parse the request body to get the topic name and message. p := publishRequest{} if err := json.NewDecoder(r.Body).Decode(&p); err != nil { log.Printf("json.NewDecoder: %v", err) http.Error(w, "Error parsing request", http.StatusBadRequest) return } if p.Topic == "" || p.Message == "" { s := "missing 'topic' or 'message' parameter" log.Println(s) http.Error(w, s, http.StatusBadRequest) return } m := &pubsub.Message{ Data: []byte(p.Message), } // Publish and Get use r.Context() because they are only needed for this // function invocation. If this were a background function, they would use // the ctx passed as an argument. id, err := client.Topic(p.Topic).Publish(r.Context(), m).Get(r.Context()) if err != nil { log.Printf("topic(%s).Publish.Get: %v", p.Topic, err) http.Error(w, "Error publishing message", http.StatusInternalServerError) return } fmt.Fprintf(w, "Message published: %v", id) }
Restablecimiento de conexiones salientes
Las transmisiones de conexión entre la función y la VPC y el Internet pueden finalizarse de forma ocasional y reemplazarse cuando se reinicia o actualiza la infraestructura subyacente. Si la aplicación reutiliza conexiones de larga duración, te recomendamos que la configures para que restablezca las conexiones y evite reutilizar una conexión inactiva.
Tiempo de espera de solicitudes salientes
Los sockets salientes pueden recuperarse después de 10 minutos de inactividad. Cualquier operación de socket lo mantiene en funcionamiento durante otros 10 minutos.
Prueba la carga de la función
Para medir la cantidad conexiones que ejecuta en promedio la función, impleméntala como una función de HTTP y usa un framework de prueba de rendimiento para invocarla con una cierta cantidad de QPS. Una opción posible es Artillery, que puedes invocar con una sola línea:
$ artillery quick -d 300 -r 30 URL
Con este comando, se recupera la URL dada a 30 QPS durante 300 segundos.
Después de ejecutar la prueba, verifica el uso de la cuota de conexión en la página de cuotas de la API de Cloud Run Functions en la consola de Google Cloud . Si el uso se mantiene alrededor de 30 (o sus múltiplos), significa que estás estableciendo una (o varias) conexiones en cada invocación. Después de optimizar el código, deberías ver que se establecen unas pocas conexiones (entre 10 y 30) solo al comienzo de la prueba.
También puedes comparar el costo de CPU antes y después de la optimización en el gráfico de cuota de CPU, en la misma página.