La semplicità di Cloud Functions ti consente di sviluppare rapidamente codice ed eseguirlo in un ambiente serverless. Su scala moderata, il costo di esecuzione delle funzioni è basso e l'ottimizzazione del codice potrebbe non sembrare una priorità elevata. Man mano che il deployment viene scalato, tuttavia, l'ottimizzazione del codice diventa sempre più importante.
Questo documento descrive come ottimizzare il networking per le tue funzioni. Ecco alcuni dei vantaggi dell'ottimizzazione del networking:
- Ridurre il tempo della CPU dedicato alla creazione di nuove connessioni in uscita a ogni chiamata di funzione.
- Ridurre la probabilità di esaurire le quote di connessione o DNS.
Mantenere le connessioni persistenti
Questa sezione fornisce esempi di come mantenere connessioni persistenti in una funzione. In caso contrario, le quote di connessione potrebbero esaurirsi rapidamente.
Questa sezione tratta i seguenti scenari:
- HTTP/S
- API di Google
Richieste HTTP/S
Lo snippet di codice ottimizzato riportato di seguito mostra come mantenere le connessioni persistenti anziché crearne una nuova a ogni chiamata di funzione:
Node.js
const http = require('http'); const functions = require('firebase-functions'); // Setting the `keepAlive` option to `true` keeps // connections open between function invocations const agent = new http.Agent({keepAlive: true}); exports.function = functions.https.onRequest((request, response) => { req = http.request({ host: '' , port: 80, path: '' , method: 'GET', agent: agent, // Holds the connection open after the first invocation }, res => { let rawData = ''; res.setEncoding('utf8'); res.on('data', chunk => { rawData += chunk; }); res.on('end', () => { response.status(200).send(`Data: ${rawData}`); }); }); req.on('error', e => { response.status(500).send(`Error: ${e.message}`); }); req.end(); });
Python
from firebase_functions import https_fn import requests # Create a global HTTP session (which provides connection pooling) session = requests.Session() @https_fn.on_request() def connection_pooling(request): # The URL to send the request to url = "http://example.com" # Process the request response = session.get(url) response.raise_for_status() return https_fn.Response("Success!")
Questa funzione HTTP utilizza un pool di connessioni per effettuare richieste HTTP. Accetta un
oggetto richiesta (flask.Request
) e restituisce il testo della risposta o qualsiasi insieme
di valori che può essere trasformato in un oggetto Response
utilizzando
make_response
.
Accesso alle API di Google
L'esempio seguente utilizza Cloud Pub/Sub, ma questo approccio funziona anche per altre librerie client, ad esempio Cloud Natural Language o Cloud Spanner. Tieni presente che i miglioramenti delle prestazioni potrebbero dipendere dall'implementazione attuale di determinate librerie client.
La creazione di un oggetto client Pub/Sub comporta una connessione e due query DNS per chiamata. Per evitare connessioni e query DNS non necessarie, crea l'oggetto client Pub/Sub nell'ambito globale come mostrato nell'esempio seguente:
Node.js
const PubSub = require('@google-cloud/pubsub'); const functions = require('firebase-functions'); const pubsub = PubSub(); exports.function = functions.https.onRequest((req, res) => { const topic = pubsub.topic('' ); topic.publish('Test message', err => { if (err) { res.status(500).send(`Error publishing the message: ${err}`); } else { res.status(200).send('1 message published'); } }); });
Python
import os from firebase_functions import https_fn from google.cloud import pubsub_v1 # from firebase_functions import https_fn # Create a global Pub/Sub client to avoid unneeded network activity pubsub = pubsub_v1.PublisherClient() @https_fn.on_request() def gcp_api_call(request): project = os.getenv("GCP_PROJECT") request_json = request.get_json() topic_name = request_json["topic"] topic_path = pubsub.topic_path(project, topic_name) # Process the request data = b"Test message" pubsub.publish(topic_path, data=data) return https_fn.Response("1 message published")
Questa funzione HTTP utilizza un'istanza della libreria client memorizzata nella cache per ridurre il numero di connessioni richieste per ogni chiamata di funzione. Prende un oggetto richiesta (flask.Request
) e restituisce il testo della risposta o qualsiasi insieme di valori che può essere trasformato in un oggetto Response
utilizzando make_response
.
La variabile di ambiente GCP_PROJECT
viene impostata automaticamente nel runtime Python 3.7. Nei runtime successivi, assicurati di specificarlo durante il deployment della funzione. Consulta Configurare le variabili di ambiente.
Connessioni in uscita
Timeout delle richieste in uscita
Si verifica un timeout dopo 10 minuti di inattività per le richieste dalla tua funzione alla rete VPC. Per le richieste dalla tua funzione a internet, si verifica un timeout dopo 20 minuti di inattività.
Reimpostazioni della connessione in uscita
I flussi di connessione dalla tua funzione sia alla rete VPC sia a internet possono essere occasionalmente terminati e sostituiti quando l'infrastruttura sottostante viene riavviata o aggiornata. Se la tua applicazione riutilizza connessioni di lunga durata, ti consigliamo di configurarla in modo che ristabilisca le connessioni per evitare il riutilizzo di una connessione non più attiva.
Test di carico della funzione
Per misurare il numero di connessioni eseguite in media dalla funzione, esegui il deployment come funzione HTTP e utilizza un framework di test delle prestazioni per richiamarla a un determinato QPS. Una scelta possibile è Artillery, che puoi richiamare con una sola riga:
$ artillery quick -d 300 -r 30 URL
Questo comando recupera l'URL specificato a 30 QPS per 300 secondi.
Dopo aver eseguito il test, controlla l'utilizzo della quota di connessione nella pagina Cloud Functions Quota API in Cloud Console. Se l'utilizzo è costantemente intorno a 30 (o a un suo multiplo), stai stabilendo una (o più) connessioni in ogni chiamata. Dopo aver ottimizzato il codice, dovresti vedere alcune (10-30) connessioni solo all'inizio del test.
Puoi anche confrontare il costo della CPU prima e dopo l'ottimizzazione nel grafico della quota di CPU nella stessa pagina.