Ambiente di esecuzione di Cloud Functions

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

Runtime

Cloud Functions supporta runtime in più lingue. Se hai eseguito il deployment delle funzioni dalla riga di comando o tramite Terraform, devi utilizzare il valore ID runtime.

Runtime Immagine di base ID runtime
Node.js 16 (consigliato) Ubuntu 18.04 Nodejs16
Node.js 14 Ubuntu 18.04 Nodejs14
Node.js 12 Ubuntu 18.04 Nodejs12
Node.js 10 Ubuntu 18.04 Nodejs10
Node.js 8 (deprecato) Ubuntu 18.04 Nodejs8
Node.js 6 (ritirato) Debian 8 Nodejs6
Python 3.9 (consigliato) 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 (anteprima) Ubuntu 18.04 java17
Java 11 (consigliato) Ubuntu 18.04 Java11
.NET Core 3.1 (consigliato) Ubuntu 18.04 dotnet3
Ruby 3.0 (anteprima) Ubuntu 18.04 rubino30
Ruby 2.7 (consigliato) Ubuntu 18.04 rubino27
Ruby 2.6 Ubuntu 18.04 rubino26
PHP 7.4 (consigliato) Ubuntu 18.04 php74

Gli aggiornamenti ai runtime vengono generalmente eseguiti automaticamente, se non diversamente specificato. Tutti i runtime ricevono aggiornamenti automatici per la versione della lingua non appena vengono resi disponibili alla community della lingua. Analogamente, Cloud Functions potrebbe applicare aggiornamenti ad altri aspetti dell'ambiente di esecuzione, ad esempio il sistema operativo o i pacchetti inclusi. Questi aggiornamenti aiutano a mantenere sicura la tua funzione.

Funzioni stateless

Cloud Functions implementa il paradigma serverless in cui esegui il tuo codice senza preoccuparti dell'infrastruttura di base, come i server o le macchine virtuali. Per consentire a Google di gestire e scalare automaticamente le funzioni, queste devono essere stateless: una chiamata funzione non deve fare affidamento sullo stato in memoria impostato da una chiamata precedente. Tuttavia, lo stato esistente può spesso essere riutilizzato per l'ottimizzazione del rendimento. Per ulteriori dettagli, consulta il consiglio Suggerimenti e consigli.

Ad esempio, il valore contatore restituito dalla funzione seguente non corrisponde al conteggio delle chiamate della funzione totale perché le chiamate potrebbero essere gestite da istanze di funzioni diverse, che non condividono variabili globali, memoria, file system o altri stati:

Node.js

// Global variable, but only shared within function instance.
let count = 0;

/**
 * HTTP Cloud Function that counts how many times
 * it is executed within a specific instance.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.executionCount = (req, res) => {
  count++;

  // Note: the total function invocation count across
  // all instances may not be equal to this value!
  res.send(`Instance execution count: ${count}`);
};

Python

# Global variable, modified within the function by using the global keyword.
count = 0

def statelessness(request):
    """
    HTTP Cloud Function that counts how many times it is executed
    within a specific instance.
    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>.
    """
    global count
    count += 1

    # Note: the total function invocation count across
    # all instances may not be equal to this value!
    return 'Instance execution count: {}'.format(count)

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
)

// count is a global variable, but only shared within a function instance.
var count = 0

// ExecutionCount is an HTTP Cloud Function that counts how many times it
// is executed within a specific instance.
func ExecutionCount(w http.ResponseWriter, r *http.Request) {
	count++

	// Note: the total function invocation count across
	// all instances may not be equal to this value!
	fmt.Fprintf(w, "Instance execution count: %d", count)
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutionCount implements HttpFunction {

  private final AtomicInteger count = new AtomicInteger(0);

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    count.getAndIncrement();

    // Note: the total function invocation count across
    // all instances may not be equal to this value!
    BufferedWriter writer = response.getWriter();
    writer.write("Instance execution count: " + count);
  }
}

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Threading;
using System.Threading.Tasks;

namespace ExecutionCount
{
    public class Function : IHttpFunction
    {
        // Note that this variable must be static, because a new instance is
        // created for each request. An alternative approach would be to use
        // dependency injection with a singleton resource injected into the function
        // constructor.
        private static int _count;

        public async Task HandleAsync(HttpContext context)
        {
            // Note: the total function invocation count across
            // all servers may not be equal to this value!
            int currentCount = Interlocked.Increment(ref _count);
            await context.Response.WriteAsync($"Server execution count: {currentCount}");
        }
    }
}

Ruby

require "functions_framework"

# Global variable, but scoped only within one function instance.
$count = 0

FunctionsFramework.http "execution_count" do |_request|
  $count += 1

  # NOTE: the total function invocation count across all instances
  # may not be equal to this value!
  "Instance execution count: #{$count}"
end

PHP

Le funzioni PHP non possono mantenere lo stato (come valori di variabile) tra le richieste.

Se devi condividere lo stato tra più chiamate di funzione, devi utilizzare un servizio come Datastore, Firestore o Cloud Storage per conservare i dati. Per un elenco completo delle opzioni di archiviazione disponibili, consulta la sezione Scegliere un'opzione di archiviazione.

Scalabilità automatica e contemporaneità

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

Ogni istanza di una funzione gestisce una sola richiesta in parallelo alla volta. Ciò significa che mentre il codice è in fase di elaborazione di una richiesta, non è possibile instradare una seconda richiesta alla stessa istanza. La richiesta originale può quindi utilizzare l'intera quantità di risorse (CPU e memoria) che hai richiesto.

Se il volume della richiesta in entrata supera il numero di istanze esistenti, Cloud Functions potrebbe 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.

Poiché le richieste in parallelo vengono elaborate da istanze di funzione diverse, non condividono le variabili o la memoria locale. Ne parliamo in dettaglio più avanti in questo documento.

Controllo del comportamento di scalabilità automatica

Cloud Functions ti consente di impostare un limite sul numero totale di istanze di funzione che possono coesistere in un dato momento. In alcuni casi, è preferibile evitare una scalabilità senza limiti. Ad esempio, la tua funzione potrebbe dipendere da una risorsa (ad esempio un database) che non è in grado di fare lo scale up allo stesso livello di Cloud Functions. Un enorme picco del volume delle richieste potrebbe determinare la creazione di istanze di funzioni da parte di Cloud Functions in modo da riuscire a tollerare il tuo database.

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 carico 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 di un'istanza di funzione (avvii a freddo) possono essere più lente delle richieste che interessano le istanze di funzione esistenti. Se la funzione riceve un carico costante, tuttavia, il numero di avvii completi è generalmente trascurabile, a meno che la funzione non si arresti spesso e richieda il riavvio dell'ambiente della funzione. Leggi la sezione Errori per scoprire come gestire correttamente gli errori ed evitare avvii completi dovuti a funzioni che si sono arrestate in modo anomalo.

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

Durata istanza funzione

L'ambiente che esegue un'istanza di funzione in genere è resiliente e riutilizzato da chiamate di funzioni successive, a meno che il numero di istanze non venga ridotto (a causa della mancanza di traffico in corso) o dell'arresto anomalo della funzione. Questo significa che quando un'esecuzione di funzione funziona, un'altra chiamata a funzione può essere gestita dalla stessa istanza di funzione. Pertanto, quando possibile, ti consigliamo di memorizzare lo stato nella cache tra le chiamate nell'ambito globale. La funzione deve essere ancora pronta per funzionare senza questa cache disponibile, poiché non è garantito che la chiamata successiva raggiunga la stessa istanza di funzione (consulta la sezione Funzioni stateless).

Ambito della funzione e ambito globale

Una chiamata a funzione singola comporta l'esecuzione solo del corpo della funzione dichiarata come punto di contatto. L'ambito globale nel file della funzione, che dovrebbe contenere la definizione della funzione, viene eseguito a ogni avvio a freddo, ma non se l'istanza è già stata inizializzata.

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 presumere che l'ambito globale sia stato eseguito esattamente una volta prima che il codice della funzione venga richiamato in una nuova istanza di funzione (e in ogni creazione successiva di una nuova istanza di funzione). Tuttavia, non devi dipendere dal numero totale o dalle tempistiche delle esecuzioni degli ambiti globali, in quanto dipendono dalla scalabilità automatica gestita da Google.

Cronologia di esecuzione della funzione

Una funzione ha accesso alle risorse richieste (CPU e memoria) solo per la durata dell'esecuzione della funzione. Non è garantito che il codice eseguito al di fuori del periodo di esecuzione venga eseguito e può essere interrotto in qualsiasi momento. Pertanto, dovresti sempre segnalare correttamente la fine dell'esecuzione della tua funzione ed evitare di eseguire codice aggiuntivo. Per indicazioni, consulta Funzioni HTTP, Funzioni in background e Funzionalità CloudEvent.

Ad esempio, il codice eseguito dopo l'invio della risposta HTTP potrebbe essere interrotto in qualsiasi momento:

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to early HTTP response
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterResponse = (req, res) => {
  res.end();

  // This statement may not execute
  console.log('Function complete!');
};

È importante tenere in considerazione la sequenza temporale di esecuzione durante l'inizializzazione dell'applicazione. Le attività in background non devono essere create nell'ambito globale durante l'inizializzazione, in quanto vengono 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 una singola chiamata in tutti i casi a causa delle differenze negli scenari di errore.

Il numero massimo o minimo di volte in cui la funzione verrà richiamata in un singolo evento dipende dal tipo di funzione:

  • Le funzioni HTTP vengono richiamate al massimo numero di volte. Ciò avviene a causa della natura sincrona delle chiamate HTTP e indica che verrà restituito qualsiasi errore nella gestione della chiamata della funzione senza nuovi tentativi. Il chiamante di una funzione HTTP dovrebbe gestire gli errori e riprovare, se necessario.

  • Le funzioni basate su eventi vengono richiamate almeno una volta. Ciò è dovuto alla natura asincrona di gestione degli eventi, in cui nessun chiamante attende la risposta. Il sistema potrebbe, in rari casi, richiamare una funzione basata su eventi più di una volta per garantire la pubblicazione dell'evento. Se una chiamata di funzione basata su eventi non riesce e restituisce un errore, non verrà richiamata a meno che non sia abilitato i nuovi tentativi in caso di errore.

Per assicurarti che la funzione si comporti correttamente durante i nuovi tentativi di esecuzione, dovresti renderla idempotente implementandola in modo che un evento generi i risultati (ed effetti collaterali) desiderati anche se viene eseguito più volte. Nel caso delle funzioni HTTP, ciò comporta anche la restituzione del valore desiderato anche se il chiamante riprova a chiamare l'endpoint della funzione HTTP. Per ulteriori informazioni su come rendere la funzione idempotente, consulta la sezione Riprovare le funzioni basate su eventi.

Errori

Il metodo consigliato per segnalare un errore dipende dal tipo di funzione:

  • Le funzioni HTTP devono restituire codici di stato HTTP appropriati che indicano un errore. Per esempi, vedi Funzioni HTTP.

  • Le funzioni basate su eventi devono registrare e restituire un messaggio di errore. Per vedere degli esempi, consulta le funzioni in background e le funzioni di CloudEvent.

Se un errore viene restituito nel modo consigliato, l'istanza di funzione che ha restituito l'errore viene etichettata come normale e può soddisfare richieste future, se necessario.

Se il codice o qualsiasi altro codice chiamato genera un'eccezione non rilevata o causa un arresto anomalo del processo attuale, l'istanza di funzione potrebbe essere riavviata prima di gestire la chiamata successiva. Ciò può causare avvii più freddi, con conseguente maggiore latenza, quindi è sconsigliato.

Per ulteriori dettagli su come segnalare errori in Cloud Functions, vedi Segnalazione degli errori.

Timeout

Il tempo di esecuzione della funzione è limitato dalla durata del timeout, che puoi specificare al momento del deployment della funzione. Per impostazione predefinita, una funzione scade dopo 1 minuto, ma puoi estendere questo periodo fino a 9 minuti.

Quando l'esecuzione della funzione supera il timeout, lo stato dell'errore viene immediatamente restituito al chiamante. Le risorse della CPU utilizzate dall'istanza della funzione con timeout sono ridotte e l'elaborazione delle richieste può essere messa in pausa immediatamente. Il lavoro in pausa potrebbe non procedere o meno per le richieste successive, causando effetti collaterali imprevisti.

Lo snippet riportato di seguito include il codice pianificato per essere eseguito 2 minuti dopo l'inizio dell'esecuzione della funzione. Se il timeout viene impostato su 1 minuto, il codice potrebbe non essere mai eseguito:

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to function execution timeout
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterTimeout = (req, res) => {
  setTimeout(() => {
    // May not execute if function's timeout is <2 minutes
    console.log('Function running...');
    res.end();
  }, 120000); // 2 minute delay
};

Python

def timeout(request):
    print('Function running...')
    time.sleep(120)

    # May not execute if function's timeout is <2 minutes
    print('Function completed!')
    return 'Function completed!'

Go


// Package tips contains tips for writing Cloud Functions in Go.
package tips

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

// Timeout sleeps for 2 minutes and may time out before finishing.
func Timeout(w http.ResponseWriter, r *http.Request) {
	log.Println("Function execution started...")
	time.Sleep(2 * time.Minute)
	log.Println("Function completed!")
	fmt.Fprintln(w, "Function completed!")
}

Java


package functions;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class AfterTimeout implements HttpFunction {
  private static final Logger logger = Logger.getLogger(AfterTimeout.class.getName());

  // Simple function to return "Hello World"
  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException, InterruptedException {
    logger.info("Function running...");
    TimeUnit.MINUTES.sleep(2);

    // May not execute if function's timeout is <2 minutes
    logger.info("Function completed!");
    BufferedWriter writer = response.getWriter();
    writer.write("Function completed!");
  }
}

In alcune circostanze, il codice riportato sopra può essere eseguito correttamente, ma in modo imprevisto. Considera lo scenario quando la funzione scade. L'istanza che gestisce la richiesta è in pausa (limitando la CPU). I lavori in attesa sono in pausa. Se una richiesta successiva viene instradata alla stessa istanza, il lavoro viene ripristinato e Function running... viene emesso nei log.

Un sintomo comune di questo comportamento è l'aspetto che i lavori e i log di un'unica richiesta vengono segnalati in una richiesta successiva. Poiché non puoi dipendere dal ripristino dei lavori in pausa, non devi fare affidamento su tale comportamento. La funzione deve invece evitare timeout utilizzando una combinazione delle seguenti tecniche:

  1. Imposta un timeout superiore al tempo di esecuzione della funzione previsto.
  2. Monitora il tempo rimanente durante l'esecuzione ed esegui la pulizia/uscita in anticipo.

Per impostare il tempo di esecuzione massimo di una funzione utilizzando l'interfaccia a riga di comando di Google Cloud, utilizza il flag --timeout al momento del deployment:

gcloud functions deploy FUNCTION_NAME --timeout=TIMEOUT FLAGS...

Nel comando riportato sopra, FLAGS... si riferisce ad altre opzioni trasmesse durante il deployment della funzione. Per un riferimento completo per il comando deploy, consulta la pagina gcloud functions deploy.

Puoi anche impostare il timeout durante la creazione della funzione in Cloud Console, come descritto di seguito:

  1. Vai alla pagina Panoramica di Cloud Functions in Cloud Console.

  2. Fai clic su Crea funzione.

  3. Compila i campi obbligatori per la funzione.

  4. Visualizza le impostazioni avanzate facendo clic su Altro.

  5. Inserisci un valore nel campo Timeout.

Memoria

Per impostare la memoria di una funzione tramite l'interfaccia a riga di comando di Google Cloud, utilizza il flag --memory con il numero di megabyte. Ad esempio:

gcloud functions deploy FUNCTION_NAME --memory=MEMORY

Per impostazione predefinita, la memoria allocata a ciascuna funzione è di 256 MB.

File system

L'ambiente di esecuzione della funzione contiene un file di funzione eseguibile, oltre a file e directory inclusi nel pacchetto delle funzioni di cui è stato eseguito il deployment, ad esempio dipendenze locali. Questi file sono disponibili in una directory di sola lettura, che può essere determinata in base alla posizione del file della funzione. Tieni presente che la directory della funzione può essere diversa dalla directory di lavoro corrente.

L'esempio seguente elenca i file che si trovano nella directory delle funzioni:

Node.js

const fs = require('fs');

/**
 * HTTP Cloud Function that lists files in the function directory
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.listFiles = (req, res) => {
  fs.readdir(__dirname, (err, files) => {
    if (err) {
      console.error(err);
      res.sendStatus(500);
    } else {
      console.log('Files', files);
      res.sendStatus(200);
    }
  });
};

Python

def list_files(request):
    import os
    from os import path

    root = path.dirname(path.abspath(__file__))
    children = os.listdir(root)
    files = [c for c in children if path.isfile(path.join(root, c))]
    return 'Files: {}'.format(files)

Go


// Package tips contains tips for writing Cloud Functions in Go.
package tips

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

// ListFiles lists the files in the current directory.
// Uses directory "serverless_function_source_code" as defined in the Go
// Functions Framework Buildpack.
// See https://github.com/GoogleCloudPlatform/buildpacks/blob/56eaad4dfe6c7bd0ecc4a175de030d2cfab9ae1c/cmd/go/functions_framework/main.go#L38.
func ListFiles(w http.ResponseWriter, r *http.Request) {
	files, err := ioutil.ReadDir("./serverless_function_source_code")
	if err != nil {
		http.Error(w, "Unable to read files", http.StatusInternalServerError)
		log.Printf("ioutil.ListFiles: %v", err)
		return
	}
	fmt.Fprintln(w, "Files:")
	for _, f := range files {
		fmt.Fprintf(w, "\t%v\n", f.Name())
	}
}

Java


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

public class FileSystem implements HttpFunction {

  // Lists the files in the current directory.
  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    File currentDirectory = new File(".");
    File[] files = currentDirectory.listFiles();
    PrintWriter writer = new PrintWriter(response.getWriter());
    writer.println("Files:");
    for (File f : files) {
      writer.printf("\t%s%n", f.getName());
    }
  }
}

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Threading.Tasks;

namespace FileSystem
{
    public class Function : IHttpFunction
    {
        public async Task HandleAsync(HttpContext context)
        {
            string[] files = Directory.GetFiles(".");

            await context.Response.WriteAsync("Files:\n");
            foreach (string file in files)
            {
                await context.Response.WriteAsync($"\t{file}\n");
            }
        }
    }
}

Ruby

require "functions_framework"

FunctionsFramework.http "concepts_filesystem" do |_request|
  files = Dir.entries "."
  "Files: #{files.join "\n"}"
end

PHP


use Psr\Http\Message\ServerRequestInterface;

function listFiles(ServerRequestInterface $request): string
{
    $contents = scandir(__DIR__);

    $output = 'Files:' . PHP_EOL;

    foreach ($contents as $file) {
        $output .= "\t" . $file . PHP_EOL;
    }

    return $output;
}

Puoi anche caricare il codice da altri file di cui è stato eseguito il deployment con la funzione.

Il file system stesso è completamente scrivibile (tranne per i file usati dal sistema operativo sottostante) e viene archiviato nella memoria dell'istanza di Cloud Functions.

Rete

La tua funzione può accedere alla rete Internet pubblica utilizzando le librerie standard offerte dal runtime o da provider di terze parti. Ad esempio, puoi chiamare un endpoint HTTP come mostrato di seguito:

Node.js

const fetch = require('node-fetch');

/**
 * HTTP Cloud Function that makes an HTTP request
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.makeRequest = async (req, res) => {
  const url = 'https://example.com'; // URL to send the request to
  const externalRes = await fetch(url);
  res.sendStatus(externalRes.ok ? 200 : 500);
};

Python

def make_request(request):
    """
    HTTP Cloud Function that makes another HTTP request.
    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>.
    """
    import requests

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

    # Process the request
    response = requests.get(url)
    response.raise_for_status()
    return 'Success!'

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
	"time"
)

var urlString = "https://example.com"

// client is used to make HTTP requests with a 10 second timeout.
// http.Clients should be reused instead of created as needed.
var client = &http.Client{
	Timeout: 10 * time.Second,
}

// MakeRequest is an example of making an HTTP request. MakeRequest uses a
// single http.Client for all requests to take advantage of connection
// pooling and caching. See https://godoc.org/net/http#Client.
func MakeRequest(w http.ResponseWriter, r *http.Request) {
	resp, err := client.Get(urlString)
	if err != nil {
		http.Error(w, "Error making request", http.StatusInternalServerError)
		return
	}
	if resp.StatusCode != http.StatusOK {
		msg := fmt.Sprintf("Bad StatusCode: %d", resp.StatusCode)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "ok")
}

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.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;

public class SendHttpRequest implements HttpFunction {

  // Create a client with some reasonable defaults. This client can be reused for multiple requests.
  // (java.net.httpClient also pools connections automatically by default.)
  private static HttpClient client =
      HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException, InterruptedException {
    // Create a GET sendHttpRequest to "http://example.com"
    String url = "http://example.com";
    var getRequest = java.net.http.HttpRequest.newBuilder().uri(URI.create(url)).GET().build();

    // Send the sendHttpRequest using the client
    var getResponse = client.send(getRequest, BodyHandlers.ofString());

    // Write the results to the output:
    var writer = new PrintWriter(response.getWriter());
    writer.printf("Received code '%s' from url '%s'.", getResponse.statusCode(), url);
  }
}

C#

using Google.Cloud.Functions.Framework;
using Google.Cloud.Functions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using System.Threading.Tasks;

namespace SendHttpRequest
{
    // Dependency injection configuration, executed during server startup.
    public class Startup : FunctionsStartup
    {
        public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services)
        {
            // Make an HttpClient available to our function via dependency injection.
            // There are many options here; see
            // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
            // for more details.
            services.AddHttpClient<IHttpFunction, Function>();
        }
    }

    // Function, decorated with the FunctionsStartup attribute to specify the startup class
    // for dependency injection.
    [FunctionsStartup(typeof(Startup))]
    public class Function : IHttpFunction
    {
        private readonly HttpClient _httpClient;

        public Function(HttpClient httpClient) =>
            _httpClient = httpClient;

        public async Task HandleAsync(HttpContext context)
        {
            string url = "http://example.com";
            using (HttpResponseMessage clientResponse = await _httpClient.GetAsync(url))
            {
                await context.Response.WriteAsync($"Received code '{(int) clientResponse.StatusCode}' from URL '{url}'.");
            }
        }
    }
}

Ruby

require "functions_framework"
require "net/http"

FunctionsFramework.http "concepts_requests" do |_request|
  url = "example.com"
  response = Net::HTTP.get_response url, "/"
  "Received code: #{response.code} from url: #{url}"
end

PHP


use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;

function makeRequest(ServerRequestInterface $request): ResponseInterface
{
    // This sample uses the GuzzleHTTP client
    // See its documentation for usage information
    // https://docs.guzzlephp.org/en/stable/

    // Specify the URL to send requests to
    $client = new Client([
        'base_uri' => 'https://example.com',
    ]);

    // Send the request
    $url_response = $client->get('/');

    $function_response = new Response(
        $url_response->getStatusCode(),
        [], // headers
        ''  // body
    );

    return $function_response;
}

Prova a riutilizzare le connessioni di rete nelle chiamate delle funzioni, come descritto in Ottimizzare il networking. Tuttavia, tieni presente che una connessione che rimane inutilizzata per 10 minuti potrebbe essere chiusa dal sistema e ulteriori tentativi di utilizzo di una connessione chiusa comporteranno un errore di reimpostazione della connessione. Il codice deve utilizzare una libreria che gestisce bene le connessioni chiuse o gestirla esplicitamente se vengono utilizzati costrutti di networking di basso livello.

Più funzioni

Ogni funzione di cui è stato eseguito il deployment viene isolata da tutte le altre, anche 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 è stato eseguito il deployment, puoi utilizzare servizi di archiviazione come Datastore, Firestore o Cloud Storage. In alternativa, puoi richiamare una funzione da un'altra, utilizzando i relativi trigger appropriati. 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.