Netzwerke optimieren

Die Einfachheit von Cloud Functions ermöglicht es Ihnen, Code schnell zu entwickeln und in einer serverlosen Umgebung auszuführen. Bei mittlerer Skalierung sind die Kosten für das Ausführen von Funktionen gering und es besteht möglicherweise kein unmittelbarer Bedarf dafür, den Code zu optimieren. Je größer Ihre Bereitstellung jedoch wird, desto wichtiger wird auch die Optimierung des Codes.

In diesem Dokument wird beschrieben, wie Sie das Netzwerk für Ihre Funktionen optimieren. Eine Netzwerkoptimierung bieten unter anderem folgende Vorteile:

  • Sie können die CPU-Zeit reduzieren, die für das Erstellen neuer ausgehender Verbindungen bei jedem Funktionsaufruf benötigt wird.
  • Sie können die Wahrscheinlichkeit verringern, dass Kontingente für Verbindungen oder DNS aufgebraucht werden.

Persistente Verbindungen aufrechterhalten

In diesem Abschnitt wird anhand von Beispielen gezeigt, wie Sie in einer Funktion persistente Verbindungen aufrechterhalten. Ohne persistente Verbindungen können die Verbindungskontingente schnell aufgebraucht sein.

Die folgenden Szenarien werden in diesem Abschnitt behandelt:

  • HTTP/S
  • Google APIs

HTTP/S-Anfragen

Mit dem folgenden optimierten Code-Snippet wird veranschaulicht, wie Sie persistente Verbindungen aufrechterhalten, anstatt bei jedem Funktionsaufruf eine neue Verbindung zu erstellen:

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!")
    

Diese HTTP-Funktion verwendet einen Verbindungspool, um HTTP-Anfragen zu senden. Sie verwendet ein Anfrageobjekt (flask.Request) und gibt den Antworttext oder eine beliebige Gruppe von Werten, die in einen Response-Objekt mithilfe von make_response

Auf Google APIs zugreifen

Im folgenden Beispiel wird Cloud Pub/Sub eingesetzt. Der Ansatz funktioniert jedoch auch für andere Clientbibliotheken, z. B. Cloud Natural Language oder Cloud Spanner. Die erzielten Leistungsverbesserungen können von der aktuellen Implementierung bestimmter Clientbibliotheken abhängen.

Beim Erstellen eines Pub/Sub-Clientobjekts werden eine Verbindung und zwei DNS-Abfragen pro Aufruf erzeugt. Erstellen Sie das Pub/Sub-Clientobjekt global, um unnötige Verbindungen und DNS-Abfragen zu vermeiden, wie im folgenden Beispiel gezeigt:

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")
    

Diese HTTP-Funktion verwendet eine im Cache gespeicherte Clientbibliotheksinstanz, die Anzahl der pro Funktionsaufruf erforderlichen Verbindungen reduzieren. Es nimmt ein Anfrageobjekt (flask.Request) an und gibt den Antworttext oder einen beliebigen Satz von Werten zurück, der mit make_response in ein Response-Objekt umgewandelt werden kann.

Die Umgebungsvariable GCP_PROJECT wird in Python automatisch festgelegt. 3.7-Laufzeit. In späteren Laufzeiten muss sie in Funktionsbereitstellung. Weitere Informationen finden Sie unter Umgebungsvariablen konfigurieren

Ausgehende Verbindungen werden zurückgesetzt

Verbindungsstreams von Ihrer Funktion zur VPC und zum Internet können Gelegentlich beendet und ersetzt, wenn die zugrunde liegende Infrastruktur neu gestartet wird oder aktualisiert werden. Wenn Ihre Anwendung langlebige Verbindungen wiederverwendet, sollten Sie Ihre Anwendung so konfigurieren, dass Verbindungen wiederhergestellt werden, um eine Wiederverwendung einer inaktiven Verbindung zu vermeiden.

Lasttests für eine Funktion ausführen

Wenn Sie messen möchten, wie viele Verbindungen im Durchschnitt von einer Funktion erstellt werden, können Sie sie einfach als HTTP-Funktion bereitstellen und in einem Leistungstest-Framework bei einer bestimmten Anzahl von Abfragen pro Sekunde aufrufen. Dazu können Sie z. B. Artillery verwenden, das sich mit einer einzigen Zeile aufrufen lässt:

$ artillery quick -d 300 -r 30 URL

Mit diesem Befehl wird die angegebene URL 300 Sekunden lang bei 30 Abfragen pro Sekunde abgerufen.

Nach dem Test prüfen Sie die Nutzungswerte Ihres Verbindungskontingents in der Cloud Console auf der Kontingentseite der Cloud Functions API. Wenn die Nutzung beständig bei 30 Abfragen oder einem Vielfachen davon liegt, erstellt die Funktion mit jedem Aufruf eine (oder mehrere) Verbindungen. Nach der Optimierung des Codes sollten nur zu Beginn des Tests ein paar wenige Verbindungen (10–30) erzeugt werden.

Auf derselben Seite können Sie im Diagramm für das CPU-Kontingent auch die CPU-Kosten vor und nach der Optimierung vergleichen.