Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Ambiente di esecuzione di Cloud Functions

Cloud Functions viene eseguito in un ambiente serverless completamente gestito in cui Google gestisce per te l'infrastruttura, i sistemi operativi e gli ambienti di runtime. Ogni funzione viene eseguita in un contesto di esecuzione sicuro isolato, scala automaticamente e ha un ciclo di vita indipendente dalle altre funzioni.

Runtime

Cloud Functions supporta runtime in più linguaggi. Se stai eseguendo il deployment delle funzioni dalla riga di comando o tramite Terraform, devi avere il valore ID runtime.

Runtime Immagine di base ID runtime
Node.js 16 (consigliato) Ubuntu 18.04 nodojs16
Node.js 14 Ubuntu 18.04 Node.js14
Node.js 12 Ubuntu 18.04 Node.js12
Node.js 10 Ubuntu 18.04 Node.js10
Node.js 8 (deprecato) Ubuntu 18.04 Node.js8
Node.js 6 (ritirato) Debian 8 Node.js6
Python 3.10 (consigliato) Ubuntu 22.04 Python310
Python 3.9 Ubuntu 18.04 Python39
Python 3.8 Ubuntu 18.04 Python38
Python 3.7 Ubuntu 18.04 Python37
Go 1.16 (consigliato) Ubuntu 18.04 Go116
Go 1.13 Ubuntu 18.04 Go113
Go 1.11 Ubuntu 18.04 Go111
Java 17 (consigliato) Ubuntu 22.04 Java (17)
Java 11 Ubuntu 18.04 Java11
.NET Core 6.0 (anteprima) Ubuntu 18.04 .net6
.NET Core 3.1 (consigliato) Ubuntu 18.04 .net3
Ruby 3.0 (consigliato) Ubuntu 18.04 rosso rubino 30
Ruby 2.7 Ubuntu 18.04 rosso rubino 27
Ruby 2.6 Ubuntu 18.04 rosso rubino 26
PHP 8.1 (consigliato) Ubuntu 18.04 php81
PHP 7.4 Ubuntu 18.04 php74

Se non diversamente indicato, gli aggiornamenti e le patch di sicurezza vengono applicati ai runtime e alle loro dipendenze quando esegui il deployment di una funzione. Sono inclusi aggiornamenti e patch realizzati da una community linguistica, che diventano disponibili dopo un periodo di test per garantirne la stabilità. Analogamente, Cloud Functions potrebbe applicare aggiornamenti ad altri aspetti dell'ambiente di esecuzione, come il sistema operativo o i pacchetti inclusi. Questi aggiornamenti contribuiscono a proteggere la funzione.

Comportamento di scalabilità automatica

Cloud Functions implementa il paradigma serverless, in cui esegui il tuo codice senza preoccuparti dell'infrastruttura sottostante, come i server o le macchine virtuali. Dopo il deployment, le funzioni vengono gestite e scalate automaticamente.

Cloud Functions gestisce le richieste in entrata assegnandole alle istanze della tua funzione. A seconda del volume di richieste, nonché del numero di istanze di funzione esistenti, Cloud Functions può assegnare una richiesta a un'istanza esistente o crearne una nuova.

Nei casi in cui il volume delle richieste in entrata supera il numero di istanze esistenti, Cloud Functions può avviare più nuove istanze per gestire le richieste. Questo comportamento di scalabilità automatica consente a Cloud Functions di gestire molte richieste in parallelo, ciascuna utilizzando un'istanza diversa della funzione.

In alcuni casi, la scalabilità senza limiti potrebbe essere indesiderata. Per risolvere questo problema, Cloud Functions consente di configurare un numero massimo di istanze che possono coesistere in un dato momento per una particolare funzione.

Condizione stateless

Per abilitare la gestione e la scalabilità automatiche delle funzioni, le funzioni devono essere stateless: una chiamata funzione non deve fare affidamento sullo stato in memoria impostato da una chiamata precedente. Le chiamate potrebbero essere gestite da istanze di funzioni diverse, che non condividono variabili globali, memoria, file system o altri stati.

Se devi condividere lo stato tra le chiamate delle funzioni, la funzione deve utilizzare un servizio come Memorystore, Datastore, Firestore o Cloud Storage per conservare i dati. Consulta i database Google Cloud e i prodotti di archiviazione Google Cloud per ulteriori informazioni sulle opzioni di database e archiviazione fornite da Google Cloud.

Contemporaneità

Ogni istanza di una funzione gestisce solo una richiesta in contemporanea alla volta. Ciò significa che mentre il codice sta elaborando una richiesta, non è possibile che una seconda richiesta venga instradata alla stessa istanza. Di conseguenza, la richiesta originale può utilizzare la quantità completa di risorse (memoria e CPU) che assegni.

Poiché le richieste in parallelo vengono elaborate da istanze di funzione diverse, non condividono variabili o memoria locale. Per ulteriori informazioni, consulta le pagine relative a statelessness e durata di istanza delle funzioni.

Avvio completo

Una nuova istanza di funzione viene avviata in due casi:

  • Quando esegui il deployment della funzione.

  • Quando viene creata automaticamente una nuova istanza di funzione per fare lo scale up fino al caricamento oppure occasionalmente per sostituire un'istanza esistente.

L'avvio di una nuova istanza di funzione comporta il caricamento del runtime e del codice. Le richieste che includono l'avvio dell'istanza di funzione, chiamata avvio a freddo, possono essere più lente delle richieste instradate alle istanze di funzione esistenti. Se la tua funzione riceve un carico costante, tuttavia, il numero di avvii completi è generalmente trascurabile, a meno che la funzione non si arresti frequentemente e richieda il riavvio dell'ambiente della funzione.

Se il codice della funzione genera un'eccezione non rilevata o causa l'arresto anomalo del processo corrente, l'istanza della funzione potrebbe essere riavviata. Ciò può determinare più avvii completi, con una conseguente latenza maggiore, quindi consigliamo di individuare le eccezioni ed evitare così la terminazione del processo attuale. Consulta la pagina Segnalazione degli errori per una discussione su come gestire e segnalare gli errori in Cloud Functions.

Se la tua funzione è sensibile alla latenza, valuta la possibilità di impostare un numero minimo di istanze per evitare avvii completi.

Durata di una istanza di funzione

Le istanze delle funzioni sono in genere resilienti e riutilizzate dalle chiamate delle funzioni successive, a meno che il numero di istanze non venga ridotto per mancanza di traffico continuo o per l'arresto anomalo della funzione. Ciò significa che al termine dell'esecuzione di una funzione, un'altra chiamata può essere gestita dalla stessa istanza di funzione.

Confronto tra ambito della funzione e ambito globale

Una chiamata a funzione singola comporta l'esecuzione del solo corpo della funzione dichiarato come punto di ingresso. L'ambito globale del codice sorgente della funzione viene eseguito solo in caso di avvio a freddo e non in istanze già inizializzate.

Node.js

// Global (instance-wide) scope
// This computation runs at instance cold-start
const instanceVar = heavyComputation();

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
exports.scopeDemo = (req, res) => {
  // Per-function scope
  // This computation runs every time this function is called
  const functionVar = lightComputation();

  res.send(`Per instance: ${instanceVar}, per function: ${functionVar}`);
};

Python

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()

def scope_demo(request):
    """
    HTTP Cloud Function that declares a variable.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Per-function scope
    # This computation runs every time this function is called
    function_var = light_computation()
    return 'Instance: {}; function: {}'.format(instance_var, function_var)

Go


// h is in the global (instance-wide) scope.
var h string

// init runs during package initialization. So, this will only run during an
// an instance's cold start.
func init() {
	h = heavyComputation()
}

// ScopeDemo is an example of using globally and locally
// scoped variables in a function.
func ScopeDemo(w http.ResponseWriter, r *http.Request) {
	l := lightComputation()
	fmt.Fprintf(w, "Global: %q, Local: %q", h, l)
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;

public class Scopes implements HttpFunction {
  // Global (instance-wide) scope
  // This computation runs at instance cold-start.
  // Warning: Class variables used in functions code must be thread-safe.
  private static final int INSTANCE_VAR = heavyComputation();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Per-function scope
    // This computation runs every time this function is called
    int functionVar = lightComputation();

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Instance: %s; function: %s", INSTANCE_VAR, functionVar);
  }

  private static int lightComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).sum();
  }

  private static int heavyComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).reduce((t, x) -> t * x).getAsInt();
  }
}

Ruby

# Global (instance-wide) scope.
# This block runs on cold start, before any function is invoked.
#
# Note: It is usually best to run global initialization in an on_startup block
# instead at the top level of the Ruby file. This is because top-level code
# could be executed to verify the function during deployment, whereas an
# on_startup block is run only when an actual function instance is starting up.
FunctionsFramework.on_startup do
  instance_data = perform_heavy_computation

  # To pass data into function invocations, the best practice is to set a
  # key-value pair using the Ruby Function Framework's built-in "set_global"
  # method. Functions can call the "global" method to retrieve the data by key.
  # (You can also use Ruby global variables or "toplevel" local variables, but
  # they can make it difficult to isolate global data for testing.)
  set_global :my_instance_data, instance_data
end

FunctionsFramework.http "tips_scopes" do |_request|
  # Per-function scope.
  # This method is called every time this function is called.
  invocation_data = perform_light_computation

  # Retrieve the data computed by the on_startup block.
  instance_data = global :my_instance_data

  "instance: #{instance_data}; function: #{invocation_data}"
end

Puoi utilizzare le variabili globali come ottimizzazione delle prestazioni, ma non devi fare affidamento sullo stato impostato nell'ambito globale in base alle chiamate di funzione precedenti. Per ulteriori informazioni, consulta la sezione Statelessness.

Puoi supporre che per ogni istanza della funzione l'ambito globale sia stato eseguito esattamente una volta prima di richiamare il codice della funzione. Tuttavia, non devi dipendere dal numero totale o dalle tempistiche delle esecuzioni degli ambiti globali, poiché possono variare a seconda dell'attività di scalabilità automatica.

Cronologia di esecuzione della funzione

Una funzione ha accesso alle sue risorse allocate (memoria e CPU) solo per la durata dell'esecuzione della funzione. L'esecuzione del codice al di fuori del periodo di esecuzione non è garantita e può essere interrotta in qualsiasi momento. Di conseguenza, devi sempre segnalare correttamente il termine dell'esecuzione della funzione ed evitare di eseguire codice al di fuori di essa. Per indicazioni, consulta le funzioni HTTP, le funzioni in background e le funzioni CloudEvent.

L'esecuzione della funzione è soggetta anche alla durata del timeout della funzione. Per ulteriori informazioni, consulta la pagina Timeout della funzione.

Tieni conto della sequenza temporale di esecuzione durante l'inizializzazione dell'applicazione. Le attività in background non devono essere create nell'ambito globale durante l'inizializzazione, poiché verranno eseguite al di fuori della durata di una richiesta.

Garanzie di esecuzione

In genere, le funzioni vengono richiamate una volta per ogni evento in entrata. Tuttavia, Cloud Functions non garantisce un'unica chiamata in tutti i casi a causa delle differenze negli scenari di errore.

Il numero massimo o minimo di volte in cui la funzione può essere richiamata per un singolo evento dipende dal tipo di funzione:

  • Le funzioni HTTP vengono richiamate al massimo. Ciò è dovuto alla natura sincrona delle chiamate HTTP e significa che qualsiasi errore che si verifica durante la chiamata alla funzione verrà restituito senza riprovare. Il chiamante di una funzione HTTP dovrebbe gestire gli errori e riprovare, se necessario.

  • Le funzioni basate sugli eventi vengono richiamate almeno una volta. Ciò è dovuto alla natura asincrona degli eventi, in cui non è presente alcun chiamante in attesa della risposta. Il sistema potrebbe, in rari casi, richiamare una funzione basata su eventi più di una volta per garantire la distribuzione dell'evento. Se una chiamata di funzione basata su eventi non riesce e restituisce un errore, la funzione non verrà richiamata per la funzione, a meno che non vengano abilitati nuovi tentativi in caso di errore.

Per assicurarti che la funzione funzioni correttamente durante i nuovi tentativi di esecuzione, devi renderla idempotente implementandola in modo che i risultati desiderati (e gli effetti collaterali) vengano generati anche se un evento viene pubblicato più volte. Nel caso delle funzioni HTTP, ciò significa anche restituire il valore desiderato anche se il chiamante riprova a chiamare l'endpoint della funzione HTTP. Per ulteriori informazioni su come rendere idempotente la tua funzione, consulta Riprovazione di funzioni basate su eventi.

Memoria e file system

Ogni funzione ha una certa quantità di memoria allocata per il suo utilizzo. Puoi configurare la quantità di memoria al momento del deployment. Per ulteriori informazioni, consulta i limiti di memoria.

L'ambiente di esecuzione della funzione include un file system in memoria che contiene i file di origine e le directory di cui è stato eseguito il deployment nella funzione (vedi Struttura del codice sorgente). La directory contenente i file di origine è di sola lettura, ma il resto del file system può essere scritto (tranne per i file utilizzati dal sistema operativo). L'utilizzo del file system contribuisce all'utilizzo della memoria di una funzione.

La tua funzione può interagire con il file system utilizzando i metodi standard in ogni linguaggio di programmazione.

Rete

La funzione può accedere alla rete Internet pubblica utilizzando metodi standard in ogni linguaggio di programmazione, tramite librerie integrate offerte dal runtime o librerie di terze parti che includi come dipendenze.

Prova a riutilizzare le connessioni di rete nelle chiamate delle funzioni, come descritto in Ottimizzazione delle reti. Tuttavia, tieni presente che una connessione che rimane inutilizzata per 10 minuti potrebbe essere chiusa dal sistema e ulteriori tentativi di utilizzare una connessione chiusa comporteranno un errore di reimpostazione della connessione. Il tuo codice dovrebbe utilizzare una libreria che gestisce bene le connessioni chiuse o farle gestire in modo esplicito se utilizzi costrutti di networking di basso livello.

Isolamento delle funzioni

Ogni funzione di cui è stato eseguito il deployment viene isolata da tutte le altre funzioni, comprese quelle di cui è stato eseguito il deployment dallo stesso file di origine. In particolare, non condividono memoria, variabili globali, file system o altri stati.

Per condividere i dati tra le funzioni di cui hai eseguito il deployment, puoi utilizzare servizi come Memorystore, Datastore, Firestore o Cloud Storage. In alternativa, puoi richiamare una funzione da un'altra utilizzando gli attivatori appropriati e trasmettendo i dati necessari. Ad esempio, effettua una richiesta HTTP all'endpoint di una funzione HTTP o pubblica un messaggio in un argomento Pub/Sub per attivare una funzione Pub/Sub.