Optimiser la mise en réseau (1re génération)
La simplicité de Cloud Run Functions vous permet de développer rapidement du code et de l'exécuter dans un environnement sans serveur. À une échelle modérée, le coût d'exécution des fonctions est faible et l'optimisation de votre code ne semble pas être la priorité. Au fur et à mesure que votre déploiement évolue, l'optimisation de votre code gagne en importance.
Ce document décrit comment optimiser la mise en réseau de vos fonctions. L'optimisation de la mise en réseau présente des avantages, dont en voici une sélection :
- Elle diminue le temps CPU passé à établir de nouvelles connexions à chaque appel de fonction
- Elle réduit le risque de manquer de quotas DNS ou de connexion.
Gérer les connexions persistantes
Cette section donne des exemples de maintien de connexions persistantes dans une fonction. À défaut, vous risquez d’épuiser rapidement les quotas de connexion.
Cette section porte sur les situations suivantes :
- HTTP/S
- API Google
Requêtes HTTP/S
L'extrait de code optimisé ci-dessous montre comment maintenir des connexions persistantes plutôt que de créer une connexion à chaque appel de fonction :
Node.js
Python
Go
PHP
Nous vous recommandons d'utiliser le framework HTTP PHP de Guzzle pour envoyer des requêtes HTTP, car il gère automatiquement les connexions persistantes.
Accéder aux API Google
L'exemple ci-dessous utilise Cloud Pub/Sub, mais cette approche fonctionne également pour d'autres bibliothèques clientes, par exemple, Cloud Natural Language ou Cloud Spanner. Notez que les améliorations de performances peuvent dépendre de la mise en œuvre actuelle de certaines bibliothèques clientes spécifiques.
La création d'un objet client Pub/Sub donne lieu à une connexion et deux requêtes DNS par appel. Pour éviter les connexions et les requêtes DNS inutiles, créez l'objet client Pub/Sub dans un champ d'application global, comme indiqué dans l'exemple suivant :
Node.js
Python
Accéder
// 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) }
Réinitialisation des connexions sortantes
Les flux de connexion de votre fonction vers un VPC et Internet peuvent parfois être arrêtés et remplacés lorsque l'infrastructure sous-jacente est redémarrée ou mise à jour. Si votre application réutilise des connexions à longue durée de vie, nous vous recommandons de configurer votre application de manière à rétablir les connexions afin d'éviter la réutilisation d'une connexion interrompue.
Délais avant expiration des requêtes sortantes
Les sockets de sortie peuvent être récupérés après 10 minutes d'inactivité. Toute opération de socket maintient le socket actif pendant 10 minutes supplémentaires.
Tester la charge de votre fonction
Pour évaluer le nombre moyen de connexions exécutées par votre fonction, déployez-la en tant que fonction HTTP et utilisez un framework de test de performance pour l'appeler à un certain nombre de requêtes par seconde. Vous pouvez, par exemple, utiliser Artillery, que vous pouvez appeler avec une seule ligne :
$ artillery quick -d 300 -r 30 URL
Cette commande extrait l'URL indiquée à 30 RPS pendant 300 secondes.
Après avoir exécuté le test, vérifiez la consommation de votre quota de connexion sur la page des quotas de l'API Cloud Run Functions dans la console Google Cloud . Si la consommation tourne toujours autour de 30 (ou de son multiple), vous établissez une (ou plusieurs) connexion à chaque appel. Après avoir optimisé votre code, vous devriez voir quelques (10-30) connexions apparaître uniquement au début du test.
Vous pouvez également comparer le coût de processeur avant et après l'optimisation sur le tracé du quota de processeur figurant sur la même page.