Ce document décrit les bonnes pratiques de conception, de mise en œuvre, de test et de déploiement de Cloud Functions.
Exactitude
Cette section décrit les bonnes pratiques générales pour la conception et la mise en œuvre de Cloud Functions.
Écrire des fonctions idempotentes
Vos fonctions doivent produire le même résultat même si elles sont appelées plusieurs fois. Cela vous permet de réessayer une invocation si l'invocation précédente échoue à mi-chemin de votre code. Pour plus d'informations, consultez Réessayer les fonctions événementielles .
Ne démarrez pas d'activités en arrière-plan
L'activité en arrière-plan est tout ce qui se passe après la fin de votre fonction. Un appel de fonction se termine une fois que la fonction est renvoyée ou signale l'achèvement, par exemple en appelant l'argument callback
dans les fonctions événementielles Node.js. Tout code exécuté après une terminaison gracieuse ne peut pas accéder au processeur et ne progressera pas.
De plus, lorsqu'un appel ultérieur est exécuté dans le même environnement, votre activité en arrière-plan reprend, interférant avec le nouvel appel. Cela peut entraîner un comportement inattendu et des erreurs difficiles à diagnostiquer. L'accès au réseau après la fin d'une fonction entraîne généralement la réinitialisation des connexions (code d'erreur ECONNRESET
).
L'activité en arrière-plan peut souvent être détectée dans les journaux d'appels individuels, en trouvant tout ce qui est enregistré après la ligne indiquant que l'appel est terminé. L'activité d'arrière-plan peut parfois être enfouie plus profondément dans le code, en particulier lorsque des opérations asynchrones telles que des rappels ou des minuteries sont présentes. Vérifiez votre code pour vous assurer que toutes les opérations asynchrones se terminent avant de mettre fin à la fonction.
Toujours supprimer les fichiers temporaires
Le stockage sur disque local dans le répertoire temporaire est un système de fichiers en mémoire. Les fichiers que vous écrivez consomment de la mémoire disponible pour votre fonction et persistent parfois entre les appels. Ne pas supprimer explicitement ces fichiers peut éventuellement entraîner une erreur de mémoire insuffisante et un démarrage à froid ultérieur.
Vous pouvez voir la mémoire utilisée par une fonction individuelle en la sélectionnant dans la liste des fonctions de la console GCP et en choisissant le diagramme d'utilisation de la mémoire .
N'essayez pas d'écrire en dehors du répertoire temporaire et assurez-vous d'utiliser des méthodes indépendantes de la plate-forme/du système d'exploitation pour construire des chemins de fichiers.
Vous pouvez réduire les besoins en mémoire lors du traitement de fichiers plus volumineux à l'aide du pipelining. Par exemple, vous pouvez traiter un fichier sur Cloud Storage en créant un flux de lecture, en le faisant passer par un processus basé sur le flux et en écrivant le flux de sortie directement dans Cloud Storage.
Outils
Cette section fournit des instructions sur l'utilisation des outils pour mettre en œuvre, tester et interagir avec Cloud Functions.
Développement local
Le déploiement d'une fonction prend un peu de temps, il est donc souvent plus rapide de tester le code de votre fonction localement.
Les développeurs Firebase peuvent utiliser l' émulateur Firebase CLI Cloud Functions .Utilisez Sendgrid pour envoyer des e-mails
Cloud Functions n'autorise pas les connexions sortantes sur le port 25, vous ne pouvez donc pas établir de connexions non sécurisées à un serveur SMTP. La méthode recommandée pour envoyer des e-mails est d'utiliser SendGrid . Vous pouvez trouver d'autres options d'envoi d'e-mails dans le didacticiel d'envoi d'e-mails à partir d'une instance pour Google Compute Engine.
Performance
Cette section décrit les meilleures pratiques pour optimiser les performances.
Utilisez les dépendances à bon escient
Étant donné que les fonctions sont sans état, l'environnement d'exécution est souvent initialisé à partir de zéro (lors de ce que l'on appelle un démarrage à froid ). Lorsqu'un démarrage à froid se produit, le contexte global de la fonction est évalué.
Si vos fonctions importent des modules, le temps de chargement de ces modules peut augmenter la latence d'appel lors d'un démarrage à froid. Vous pouvez réduire cette latence, ainsi que le temps nécessaire au déploiement de votre fonction, en chargeant correctement les dépendances et en ne chargeant pas les dépendances que votre fonction n'utilise pas.
Utiliser des variables globales pour réutiliser des objets dans de futures invocations
Il n'y a aucune garantie que l'état d'une fonction Cloud sera préservé pour les appels futurs. Cependant, Cloud Functions recycle souvent l'environnement d'exécution d'un appel précédent. Si vous déclarez une variable dans la portée globale, sa valeur peut être réutilisée dans les invocations suivantes sans avoir à être recalculée.
De cette façon, vous pouvez mettre en cache des objets qui peuvent être coûteux à recréer à chaque appel de fonction. Le déplacement de ces objets du corps de la fonction vers la portée globale peut entraîner des améliorations significatives des performances. L'exemple suivant crée un objet lourd une seule fois par instance de fonction et le partage entre tous les appels de fonction atteignant l'instance donnée :
console.log('Global scope');
const perInstance = heavyComputation();
const functions = require('firebase-functions');
exports.function = functions.https.onRequest((req, res) => {
console.log('Function invocation');
const perFunction = lightweightComputation();
res.send(`Per instance: ${perInstance}, per function: ${perFunction}`);
});
Il est particulièrement important de mettre en cache les connexions réseau, les références de bibliothèque et les objets client API dans une portée globale. Voir Optimisation de la mise en réseau pour des exemples.
Faire une initialisation paresseuse des variables globales
Si vous initialisez des variables dans une portée globale, le code d'initialisation sera toujours exécuté via un appel de démarrage à froid, ce qui augmentera la latence de votre fonction. Dans certains cas, cela provoque des délais d'attente intermittents pour les services appelés s'ils ne sont pas gérés correctement dans un bloc try
/ catch
. Si certains objets ne sont pas utilisés dans tous les chemins de code, envisagez de les initialiser paresseusement à la demande :
const functions = require('firebase-functions');
let myCostlyVariable;
exports.function = functions.https.onRequest((req, res) => {
doUsualWork();
if(unlikelyCondition()){
myCostlyVariable = myCostlyVariable || buildCostlyVariable();
}
res.status(200).send('OK');
});
Ceci est particulièrement important si vous définissez plusieurs fonctions dans un seul fichier et que différentes fonctions utilisent différentes variables. À moins que vous n'utilisiez l'initialisation différée, vous risquez de gaspiller des ressources sur des variables qui sont initialisées mais jamais utilisées.
Réduisez les démarrages à froid en définissant un nombre minimum d'instances
Par défaut, Cloud Functions adapte le nombre d'instances en fonction du nombre de requêtes entrantes. Vous pouvez modifier ce comportement par défaut en définissant un nombre minimum d'instances que Cloud Functions doit maintenir prêtes à traiter les requêtes. La définition d'un nombre minimum d'instances réduit les démarrages à froid de votre application. Nous vous recommandons de définir un nombre minimum d'instances si votre application est sensible à la latence.
Voir Contrôler le comportement de mise à l'échelle pour plus d'informations sur ces options d'exécution.Ressources additionnelles
Découvrez-en plus sur l'optimisation des performances dans la vidéo "Google Cloud Performance Atlas" Cloud Functions Cold Boot Time .