Catch up on highlights from Firebase at Google I/O 2023. Learn more

Suggerimenti e trucchi

Questo documento descrive le best practice per la progettazione, l'implementazione, il test e la distribuzione di Cloud Functions.

Correttezza

Questa sezione descrive le best practice generali per la progettazione e l'implementazione di Cloud Functions.

Scrivere funzioni idempotenti

Le tue funzioni dovrebbero produrre lo stesso risultato anche se vengono chiamate più volte. Ciò consente di ritentare un'invocazione se l'invocazione precedente non riesce a metà del codice. Per ulteriori informazioni, vedere nuovo tentativo di funzioni guidate da eventi .

Non avviare attività in background

L'attività in background è tutto ciò che accade dopo che la tua funzione è terminata. Una chiamata di funzione termina quando la funzione restituisce o segnala in altro modo il completamento, ad esempio chiamando l'argomento callback nelle funzioni guidate da eventi di Node.js. Qualsiasi codice eseguito dopo la chiusura regolare non può accedere alla CPU e non farà alcun progresso.

Inoltre, quando viene eseguita una chiamata successiva nello stesso ambiente, l'attività in background riprende, interferendo con la nuova chiamata. Ciò può portare a comportamenti imprevisti ed errori difficili da diagnosticare. L'accesso alla rete dopo che una funzione è terminata di solito comporta il ripristino delle connessioni (codice di errore ECONNRESET ).

L'attività in background può spesso essere rilevata nei log da singole invocazioni, trovando tutto ciò che viene registrato dopo la riga che indica che l'invocazione è terminata. L'attività in background a volte può essere sepolta più in profondità nel codice, specialmente quando sono presenti operazioni asincrone come callback o timer. Esamina il codice per assicurarti che tutte le operazioni asincrone vengano completate prima di terminare la funzione.

Elimina sempre i file temporanei

L'archiviazione su disco locale nella directory temporanea è un file system in memoria. I file che scrivi consumano la memoria disponibile per la tua funzione e talvolta persistono tra le chiamate. La mancata eliminazione esplicita di questi file potrebbe causare un errore di memoria insufficiente e un successivo avvio a freddo.

Puoi visualizzare la memoria utilizzata da una singola funzione selezionandola nell'elenco delle funzioni nella console di GCP e scegliendo il grafico sull'utilizzo della memoria .

Non tentare di scrivere al di fuori della directory temporanea e assicurati di utilizzare metodi indipendenti dalla piattaforma/dal sistema operativo per creare percorsi di file.

È possibile ridurre i requisiti di memoria durante l'elaborazione di file più grandi utilizzando il pipelining. Ad esempio, puoi elaborare un file su Cloud Storage creando un flusso di lettura, passandolo attraverso un processo basato sul flusso e scrivendo il flusso di output direttamente su Cloud Storage.

Utensili

Questa sezione fornisce linee guida su come utilizzare gli strumenti per implementare, testare e interagire con Cloud Functions.

Sviluppo locale

La distribuzione della funzione richiede un po' di tempo, quindi spesso è più veloce testare il codice della funzione in locale.

Gli sviluppatori Firebase possono utilizzare Firebase CLI Cloud Functions Emulator .

Usa Sendgrid per inviare e-mail

Cloud Functions non consente connessioni in uscita sulla porta 25, pertanto non è possibile effettuare connessioni non sicure a un server SMTP. Il modo consigliato per inviare e-mail è utilizzare SendGrid . Puoi trovare altre opzioni per l'invio di email nel tutorial Invio di email da un'istanza per Google Compute Engine.

Prestazione

Questa sezione descrive le best practice per l'ottimizzazione delle prestazioni.

Usa le dipendenze con saggezza

Poiché le funzioni sono senza stato, l'ambiente di esecuzione viene spesso inizializzato da zero (durante quello che è noto come avvio a freddo ). Quando si verifica un avvio a freddo, viene valutato il contesto globale della funzione.

Se le tue funzioni importano moduli, il tempo di caricamento per tali moduli può aumentare la latenza di chiamata durante un avvio a freddo. Puoi ridurre questa latenza, così come il tempo necessario per distribuire la tua funzione, caricando correttamente le dipendenze e non caricando le dipendenze che la tua funzione non usa.

Utilizzare le variabili globali per riutilizzare gli oggetti nelle chiamate future

Non vi è alcuna garanzia che lo stato di una funzione cloud venga preservato per future invocazioni. Tuttavia, Cloud Functions spesso ricicla l'ambiente di esecuzione di una chiamata precedente. Se dichiari una variabile in ambito globale, il suo valore può essere riutilizzato nelle chiamate successive senza dover essere ricalcolato.

In questo modo è possibile memorizzare nella cache oggetti che potrebbero essere costosi da ricreare a ogni chiamata di funzione. Lo spostamento di tali oggetti dal corpo della funzione all'ambito globale può comportare significativi miglioramenti delle prestazioni. L'esempio seguente crea un oggetto pesante solo una volta per istanza di funzione e lo condivide tra tutte le chiamate di funzione che raggiungono l'istanza data:

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}`);
});

È particolarmente importante memorizzare nella cache le connessioni di rete, i riferimenti alle librerie e gli oggetti client API nell'ambito globale. Vedere Ottimizzazione della rete per esempi.

Inizializzazione pigra delle variabili globali

Se inizializzi le variabili nell'ambito globale, il codice di inizializzazione verrà sempre eseguito tramite una chiamata di avvio a freddo, aumentando la latenza della tua funzione. In alcuni casi, ciò causa timeout intermittenti ai servizi chiamati se non vengono gestiti in modo appropriato in un blocco try / catch . Se alcuni oggetti non vengono utilizzati in tutti i percorsi di codice, considera di inizializzarli pigramente su richiesta:

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');
});

Ciò è particolarmente importante se si definiscono più funzioni in un singolo file e funzioni diverse utilizzano variabili diverse. A meno che non utilizzi l'inizializzazione pigra, potresti sprecare risorse su variabili inizializzate ma mai utilizzate.

Riduci gli avviamenti a freddo impostando un numero minimo di istanze

Per impostazione predefinita, Cloud Functions ridimensiona il numero di istanze in base al numero di richieste in entrata. Puoi modificare questo comportamento predefinito impostando un numero minimo di istanze che Cloud Functions deve tenere pronte per soddisfare le richieste. L'impostazione di un numero minimo di istanze riduce gli avviamenti a freddo dell'applicazione. Ti consigliamo di impostare un numero minimo di istanze se la tua applicazione è sensibile alla latenza.

Per ulteriori informazioni su queste opzioni di runtime, consulta Comportamento del ridimensionamento dei controlli .

Risorse addizionali

Scopri di più sull'ottimizzazione delle prestazioni nel video "Google Cloud Performance Atlas" Cloud Functions Cold Boot Time .