Vernetzung optimieren

Dank der Einfachheit von Cloud Functions können Sie schnell Code entwickeln und ihn in einer serverlosen Umgebung ausführen. Bei mittlerem Maßstab sind die Kosten für die Ausführung von Funktionen gering und die Optimierung Ihres Codes scheint möglicherweise keine hohe Priorität zu haben. Mit zunehmender Größe Ihrer Bereitstellung wird die Optimierung Ihres Codes jedoch immer wichtiger.

In diesem Dokument wird beschrieben, wie Sie die Vernetzung für Ihre Funktionen optimieren. Zu den Vorteilen einer optimierten Vernetzung zählen:

  • Reduzieren Sie die CPU-Zeit, die für den Aufbau neuer Verbindungen bei jedem Funktionsaufruf aufgewendet wird.
  • Reduzieren Sie die Wahrscheinlichkeit, dass Ihnen die Verbindungs- oder DNS- Kontingente ausgehen.

Aufrechterhaltung dauerhafter Verbindungen

In diesem Abschnitt finden Sie Beispiele für die Aufrechterhaltung dauerhafter Verbindungen in einer Funktion. Andernfalls kann es zu einer schnellen Erschöpfung der Verbindungskontingente kommen.

Die folgenden Szenarien werden in diesem Abschnitt behandelt:

  • HTTP/S
  • Google-APIs

HTTP/S-Anfragen

Der folgende optimierte Codeausschnitt zeigt, wie Sie dauerhafte 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 stellen. Es nimmt ein Anforderungsobjekt ( flask.Request ) und gibt den Antworttext oder einen beliebigen Satz von Werten zurück, der mit make_response in ein Response umgewandelt werden kann.

Zugriff auf Google APIs

Im folgenden Beispiel wird Cloud Pub/Sub verwendet, dieser Ansatz funktioniert jedoch auch für andere Client-Bibliotheken, beispielsweise Cloud Natural Language oder Cloud Spanner . Beachten Sie, dass Leistungsverbesserungen von der aktuellen Implementierung bestimmter Clientbibliotheken abhängen können.

Das Erstellen eines Pub/Sub-Clientobjekts führt zu einer Verbindung und zwei DNS-Abfragen pro Aufruf. Um unnötige Verbindungen und DNS-Abfragen zu vermeiden, erstellen Sie das Pub/Sub-Clientobjekt im globalen Bereich, 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 zwischengespeicherte Clientbibliotheksinstanz, um die Anzahl der pro Funktionsaufruf erforderlichen Verbindungen zu reduzieren. Es nimmt ein Anforderungsobjekt ( flask.Request ) und gibt den Antworttext oder einen beliebigen Satz von Werten zurück, der mit make_response in ein Response umgewandelt werden kann.

Die Umgebungsvariable GCP_PROJECT wird in der Python 3.7-Laufzeit automatisch festgelegt. Stellen Sie in späteren Laufzeiten sicher, dass Sie es bei der Funktionsbereitstellung angeben. Siehe Umgebungsvariablen konfigurieren .

Belastungstest Ihrer Funktion

Um zu messen, wie viele Verbindungen Ihre Funktion im Durchschnitt ausführt, stellen Sie sie einfach als HTTP-Funktion bereit und verwenden Sie ein Leistungstest-Framework, um sie bei bestimmten QPS aufzurufen. Eine mögliche Wahl ist Artillery , die Sie mit einer einzigen Zeile aufrufen können:

$ artillery quick -d 300 -r 30 URL

Dieser Befehl ruft die angegebene URL 300 Sekunden lang mit 30 QPS ab.

Überprüfen Sie nach der Durchführung des Tests die Nutzung Ihres Verbindungskontingents auf der Cloud Functions API-Kontingentseite in der Cloud Console. Wenn die Nutzung konstant bei etwa 30 (oder einem Vielfachen davon) liegt, stellen Sie bei jedem Aufruf eine (oder mehrere) Verbindungen her. Nachdem Sie Ihren Code optimiert haben, sollten nur zu Beginn des Tests einige (10–30) Verbindungen auftreten.

Sie können die CPU-Kosten auch vor und nach der Optimierung im CPU-Kontingentdiagramm auf derselben Seite vergleichen.