Suggerimenti e trucchi

Questo documento descrive le migliori pratiche 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.

Scrivi funzioni idempotenti

Le tue funzioni dovrebbero produrre lo stesso risultato anche se vengono chiamate più volte. Ciò ti consente di riprovare una chiamata se la chiamata precedente non riesce a metà del codice. Per ulteriori informazioni, vedere Riprova con le funzioni in background .

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 di callback nelle funzioni in background di Node.js. Qualsiasi codice eseguito dopo la terminazione regolare non può accedere alla CPU e non farà alcun progresso.

Inoltre, quando una successiva chiamata viene eseguita 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 al termine di una funzione di solito comporta il ripristino delle connessioni (codice di errore ECONNRESET ).

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

Elimina sempre i file temporanei

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

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

Non tentare di scrivere al di fuori della directory temporanea e accertarsi 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 di grandi dimensioni 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.

Strumenti

Questa sezione fornisce linee guida su come utilizzare gli strumenti per implementare, testare e interagire con le funzioni cloud.

Sviluppo locale

La distribuzione delle funzioni 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, quindi non puoi 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 migliori pratiche per l'ottimizzazione delle prestazioni.

Usa saggiamente le dipendenze

Poiché le funzioni sono stateless, 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 di tali moduli può aumentare la latenza di chiamata durante un avvio a freddo. È possibile ridurre questa latenza, nonché il tempo necessario per distribuire la funzione, caricando correttamente le dipendenze e non caricando le dipendenze non utilizzate dalla funzione.

Usa le variabili globali per riutilizzare gli oggetti nelle chiamate future

Non vi è alcuna garanzia che lo stato di una Funzione Cloud venga preservato per le chiamate future. Tuttavia, Cloud Functions ricicla spesso 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 puoi memorizzare nella cache oggetti che potrebbero essere costosi da ricreare su ogni chiamata di funzione. Lo spostamento di tali oggetti dal corpo della funzione all'ambito globale può comportare miglioramenti significativi delle prestazioni. L'esempio seguente crea un oggetto pesante solo una volta per istanza di funzione e lo condivide su tutte le chiamate di funzione che raggiungono l'istanza specificata:

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 si inizializzano variabili in ambito globale, il codice di inizializzazione verrà sempre eseguito tramite una chiamata di avvio a freddo, aumentando la latenza della funzione. In alcuni casi, ciò provoca timeout intermittenti per i 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 unico 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 a 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.

Vedere Controllo del comportamento di ridimensionamento per ulteriori informazioni su queste opzioni di runtime.

Risorse addizionali

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