Optimiser les réseaux

La simplicité de Cloud 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 sortantes à chaque appel de fonction
  • Elle réduit le risque de manquer de quotas DNS ou de connexion.

Maintenir des 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

Demandes 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

const http = require('http');
const functions = require('firebase-functions');

// Setting the `keepAlive` option to `true` keeps
// connections open between function invocations
const agent = new http.Agent({keepAlive: true});

exports.function = functions.https.onRequest((request, response) => {
    req = http.request({
        host: '',
        port: 80,
        path: '',
        method: 'GET',
        agent: agent, // Holds the connection open after the first invocation
    }, res => {
        let rawData = '';
        res.setEncoding('utf8');
        res.on('data', chunk => { rawData += chunk; });
        res.on('end', () => {
            response.status(200).send(`Data: ${rawData}`);
        });
    });
    req.on('error', e => {
        response.status(500).send(`Error: ${e.message}`);
    });
    req.end();
});

Python

from firebase_functions import https_fn
import requests

# Create a global HTTP session (which provides connection pooling)
session = requests.Session()

@https_fn.on_request()
def connection_pooling(request):

    # The URL to send the request to
    url = "http://example.com"

    # Process the request
    response = session.get(url)
    response.raise_for_status()
    return https_fn.Response("Success!")
    

Cette fonction HTTP utilise un pool de connexions pour effectuer des requêtes HTTP. Il prend un objet de requête (flask.Request) et renvoie le texte de réponse ou tout ensemble de valeurs pouvant être transformé en objet Response à l'aide de make_response.

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 PubSub 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 PubSub dans un champ d'application global, comme indiqué dans l'échantillon ci-dessous :

Node.js

const PubSub = require('@google-cloud/pubsub');
const functions = require('firebase-functions');
const pubsub = PubSub();

exports.function = functions.https.onRequest((req, res) => {
    const topic = pubsub.topic('');

    topic.publish('Test message', err => {
        if (err) {
            res.status(500).send(`Error publishing the message: ${err}`);
        } else {
            res.status(200).send('1 message published');
        }
    });
});

Python

import os

from firebase_functions import https_fn
from google.cloud import pubsub_v1

# from firebase_functions import https_fn
# Create a global Pub/Sub client to avoid unneeded network activity
pubsub = pubsub_v1.PublisherClient()

@https_fn.on_request()
def gcp_api_call(request):

    project = os.getenv("GCP_PROJECT")
    request_json = request.get_json()

    topic_name = request_json["topic"]
    topic_path = pubsub.topic_path(project, topic_name)

    # Process the request
    data = b"Test message"
    pubsub.publish(topic_path, data=data)

    return https_fn.Response("1 message published")
    

Cette fonction HTTP utilise une instance de bibliothèque cliente en cache pour réduire le nombre de connexions requises par appel de fonction. Il prend un objet de requête (flask.Request) et renvoie le texte de réponse ou tout ensemble de valeurs pouvant être transformé en objet Response à l'aide de make_response.

La variable d'environnement GCP_PROJECT est définie automatiquement dans l'environnement d'exécution Python 3.7. Dans les environnements d'exécution ultérieurs, veillez à le spécifier lors du déploiement de la fonction. Consultez la section Configurer les variables d'environnement.

Connexions sortantes

Délais avant expiration des requêtes sortantes

Un délai avant expiration s'applique après 10 minutes d'inactivité aux requêtes provenant de votre fonction vers le réseau VPC. Pour les requêtes provenant de votre fonction vers Internet, le délai avant expiration est écoulé après 20 minutes d'inactivité.

Réinitialisation des connexions sortantes

Les flux de connexion de votre fonction vers le réseau 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.

Test de charge de votre fonction

Pour évaluer le nombre moyen de connexions exécutées par votre fonction, déployez-la simplement 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 requêtes par seconde 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 Functions dans la console 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.