Cloud Functions-Funktionen werden in einer vollständig verwalteten, serverlosen Umgebung ausgeführt, in der Google sich um die Verwaltung der Infrastruktur, Betriebssysteme und Laufzeitumgebungen kümmert. Jede Cloud Functions-Funktion wird in ihrem eigenen isolierten, sicheren Ausführungskontext bereitgestellt und automatisch skaliert. Ihr Lebenszyklus ist von dem anderer Funktionen unabhängig.
Laufzeiten
Cloud Functions unterstützt mehrere Sprachlaufzeiten. Sie benötigen den Laufzeit-ID-Wert, wenn Sie Funktionen über die Befehlszeile oder über Terraform bereitstellen.
Laufzeit | Basis-Image | Laufzeit-ID |
---|---|---|
Node.js 6 (außer Betrieb genommen) | Debian 8 | nodejs6 |
Node.js 8 (verworfen) | Ubuntu 18.04 | nodejs8 |
Node.js 10 | Ubuntu 18.04 | nodejs10 |
Node.js 12 | Ubuntu 18.04 | nodejs12 |
Node.js 14 | Ubuntu 18.04 | nodejs14 |
Python 3.7 | Ubuntu 18.04 | python37 |
Python 3.8 | Ubuntu 18.04 | python38 |
Go 1.11 (verworfen) | Ubuntu 18.04 | go111 |
Go 1.13 | Ubuntu 18.04 | go113 |
Java 11 | Ubuntu 18.04 | java11 |
.NET Core 3.1 | Ubuntu 18.04 | dotnet3 |
Ruby 2.6 | Ubuntu 18.04 | ruby26 |
Ruby 2.7 | Ubuntu 18.04 | ruby27 |
Aktualisierungen der Laufzeiten erfolgen automatisch, sofern nicht anders angegeben. Alle Laufzeiten werden automatisch an die Sprachversion angepasst, sobald sie der Sprachcommunity zur Verfügung gestellt werden. In gleicher Weise kann Cloud Functions Aktualisierungen auf andere Aspekte der Ausführungsumgebung anwenden, z. B. das Betriebssystem oder eingeschlossene Pakete. Diese Aktualisierungen tragen zur Sicherheit Ihrer Funktion bei.
Zustandslose Funktionen
Cloud Functions sind serverlos. Sie müssen sich bei der Codeausführung also nicht um die darunterliegende Infrastruktur wie Server oder virtuelle Maschinen kümmern. Damit Google die Funktionen automatisch verwalten und skalieren kann, müssen sie zustandslos sein. Ein Funktionsaufruf sollte nicht auf dem Speicherzustand basieren, der durch einen vorherigen Aufruf festgelegt wurde. Oft kann ein vorhandener Zustand jedoch wiederverwendet werden, um die Leistung zu steigern. Weitere Informationen finden Sie in den Tipps und Tricks.
Beispielsweise entspricht der von der folgenden Funktion zurückgegebene Zählerwert nicht der Gesamtzahl der Funktionsaufrufe, da Aufrufe von verschiedenen Funktionsinstanzen verarbeitet werden können, die nicht die gleichen globalen Variablen, Speicher, Dateisysteme oder andere Zustände haben:
Node.js
Python
Go
Java
C#
Ruby
Wenn Sie den Status für Funktionsaufrufe freigeben müssen, sollte die Funktion einen Dienst wie Datenspeicher, Firestore oder Cloud Storage verwenden, um Daten zu speichern. Eine vollständige Liste der verfügbaren Speicheroptionen finden Sie unter Speicheroption auswählen.
Automatische Skalierung und Gleichzeitigkeit
Cloud Functions verarbeitet eingehende Anfragen, indem diese den Instanzen Ihrer Funktion zugewiesen werden. Abhängig von der Anzahl der Anfragen sowie der Anzahl der vorhandenen Funktionsinstanzen kann Cloud Functions eine Anfrage einer vorhandenen Instanz zuweisen oder eine neue erstellen.
Jede Instanz einer Funktion verarbeitet jeweils nur eine einzige Anfrage. Das bedeutet, dass keine zweite Anfrage an dieselbe Instanz weitergeleitet werden kann, während Ihr Code eine Anfrage verarbeitet. Die ursprüngliche Anfrage kann also die gesamte von Ihnen angeforderte Menge an Ressourcen (CPU und Arbeitsspeicher) nutzen.
Wenn das Volumen der eingehenden Anfragen die Anzahl der vorhandenen Instanzen überschreitet, kann Cloud Functions mehrere neue Instanzen zur Verarbeitung von Anfragen starten. Dieses automatische Skalierungsverhalten ermöglicht es Cloud Functions, viele Anfragen parallel zu verarbeiten, wobei jede eine andere Instanz Ihrer Funktion verwendet.
Da gleichzeitige Anfragen von verschiedenen Funktionsinstanzen verarbeitet werden, teilen diese keine Variablen oder lokalen Arbeitsspeicher. Darauf wird später in diesem Dokument noch genauer eingegangen.
Autoscaling-Verhalten steuern
Mit Cloud Functions können Sie eine Grenze festlegen, wie viele Funktionsinstanzen gleichzeitig koexistieren können. In einigen Fällen ist eine unbegrenzte Skalierung nicht sinnvoll. Ihre Funktion kann z. B. von einer Ressource abhängen (wie einer Datenbank), die sich nicht im gleichen Maße wie Cloud Functions skalieren lässt. Ein starker Anstieg des Anforderungsvolumens kann dazu führen, dass Cloud Functions mehr Funktionsinstanzen erstellt, als Ihre Datenbank bewältigen kann.
Kaltstarts
Eine neue Funktionsinstanz wird in zwei Fällen gestartet:
Wenn Sie die Funktion bereitstellen
Wenn die Funktion als Reaktion auf eine gestiegene Arbeitslast oder als Ersatz für eine andere Instanz automatisch erstellt wird.
Beim Starten einer neuen Funktionsinstanz werden die Laufzeit und der Code geladen. Anfragen, die den Start einer Funktionsinstanz (Kaltstart) erfordern, benötigen möglicherweise mehr Zeit als solche, die von einer vorhandenen Funktionsinstanz verarbeitet werden können. Wenn Ihre Funktion eine konstante Arbeitslast verarbeitet, ist die Anzahl der Kaltstarts normalerweise vernachlässigbar, es sei denn, sie stürzt häufig ab und die Funktionsumgebung muss neu gestartet werden. Unter Fehler finden Sie weitere Informationen zur Fehlerbehandlung und dazu, wie sich Kaltstarts vermeiden lassen.
Lebensdauer einer Funktionsinstanz
Die Umgebung, in der eine Funktionsinstanz ausgeführt wird, ist in der Regel ausfallsicher und wird von nachfolgenden Funktionsaufrufen wiederverwendet, es sei denn, die Anzahl der Instanzen wird verkleinert (aufgrund geringer Zugriffszahlen) oder die Funktion stürzt ab. Das bedeutet, wenn eine Funktionsausführung endet, kann dieselbe Funktionsinstanz einen weiteren Funktionsaufruf verarbeiten. Deswegen ist es auch eine gute Idee, den Zustand für mehrere Aufrufe möglichst im globalen Geltungsbereich zwischenzuspeichern. Die Funktion sollte aber auch ohne den zwischengespeicherten Zustand ihre Aufgaben erfüllen können, da es keine Garantie dafür gibt, dass der nächste Aufruf bei derselben Funktionsinstanz landen wird (siehe Zustandslose Funktionen).
Funktionsbereich oder globaler Bereich
Beim Aufrufen einer einzelnen Funktion wird nur der als Einstiegspunkt deklarierte Funktionsteil ausgeführt. Der globale Gültigkeitsbereich in der Funktionsdatei, die in der Regel die Funktionsdefinition enthält, wird bei jedem Kaltstart ausgeführt, jedoch nicht, wenn die Instanz bereits initialisiert wurde.
Node.js
Python
Go
Java
Sie können davon ausgehen, dass der globale Geltungsbereich genau einmal ausgeführt wurde, bevor Ihr Funktionscode in einer neuen Funktionsinstanz (und bei jeder nachfolgenden Erstellung einer neuen Funktionsinstanz) aufgerufen wird. Eine Abhängigkeit von der Gesamtanzahl oder dem Zeitpunkt der Ausführungen im globalen Bereich ist jedoch zu vermeiden, da diese von der von Google verwalteten automatischen Skalierung abhängig sind.
Zeitlicher Ablauf der Funktionsausführung
Eine Funktion hat nur für die Dauer der Funktionsausführung Zugriff auf die angeforderten Ressourcen (CPU und Speicher). Code, der außerhalb des Ausführungszeitraums ausgeführt wird, wird nicht garantiert ausgeführt und kann jederzeit angehalten werden. Daher sollten Sie das Ende der Ausführung der Funktion immer korrekt signalisieren und vermeiden, dass Code darüber hinaus ausgeführt wird. Näheres dazu finden Sie unter HTTP-Funktionen und Hintergrundfunktionen.
Der nach dem Senden der HTTP-Antwort ausgeführte Code könnte zum Beispiel jederzeit unterbrochen werden:
Node.js
Es ist wichtig, den zeitlichen Ablauf der Ausführung bei der Initialisierung Ihrer Anwendung zu berücksichtigen. Hintergrundaufgaben sollten während der Initialisierung nicht im globalen Bereich erstellt werden, da sie außerhalb der Dauer einer Anfrage ausgeführt werden.
Ausführungsgarantien
Eine Funktion wird normalerweise einmal für jedes eingehende Ereignis aufgerufen. Cloud Functions kann den einmaligen Aufruf aufgrund von Unterschieden in den Fehlerszenarien aber nicht in allen Fällen garantieren.
Die maximale oder minimale Häufigkeit, mit der eine Funktion für ein einzelnes Ereignis aufgerufen wird, hängt von ihrem Typ ab:
HTTP-Funktionen werden höchstens einmal aufgerufen. Das liegt daran, dass HTTP-Aufrufe synchron erfolgen. Jeder Fehler bei der Verarbeitung des Funktionsaufrufs wird ohne einen erneuten Versuch zurückgegeben. Es wird erwartet, dass der Aufrufer der HTTP-Funktion die Fehler behandelt und bei Bedarf den Aufruf wiederholt.
Hintergrundfunktionen werden mindestens einmal aufgerufen. Das liegt an der asynchronen Behandlung von Ereignissen, bei denen kein Aufrufer auf die Antwort wartet. In seltenen Fällen kann das System eine Hintergrundfunktion mehr als einmal aufrufen, um die Lieferung des Ereignisses sicherzustellen. Wenn ein Aufruf einer Hintergrundfunktion aufgrund eines Fehlers fehlschlägt, wird sie nur dann wieder aufgerufen, wenn für diese Funktion Wiederholungsversuche bei Fehlern aktiviert sind.
Damit sich die Funktion bei wiederholten Ausführungsversuchen korrekt verhält, sollten Sie sie idempotent machen, indem Sie sie so implementieren, dass ein Ereignis zu den gewünschten Ergebnissen (und Nebeneffekten) führt, auch wenn es mehrmals zurückgegeben wird. Für HTTP-Funktionen bedeutet das, dass der gewünschte Wert geliefert wird, selbst wenn der Aufrufer die Aufrufe an den HTTP-Funktionsendpunkt wiederholt. Weitere Informationen dazu, wie Sie Ihre Funktion idempotent machen, finden Sie unter Hintergrundfunktionen wiederholen.
Fehler
Die richtige Art der Fehlersignalisierung hängt vom Funktionstyp ab:
HTTP-Funktionen sollten HTTP-Statuscodes zurückgeben, die den Fehler anzeigen. Beispiele finden Sie unter HTTP-Funktionen.
Hintergrundfunktionen sollten den Fehler protokollieren und eine Fehlermeldung zurückgeben. Beispiele finden Sie unter Hintergrundfunktionen.
Wird ein Fehler auf die empfohlene Weise zurückgegeben, gilt die Funktionsinstanz, die den Fehler zurückgegeben hat, als normal und kann bei Bedarf zukünftige Anfragen verarbeiten.
Wenn Ihr Code oder jeder andere Code, den Sie aufrufen, eine unbehandelte Ausnahme auslöst oder den aktuellen Prozess zum Abstürzen bringt, kann die Funktionsinstanz neu gestartet werden, bevor der nächste Aufruf verarbeitet wird. Das kann zu einer erhöhten Anzahl von Kaltstarts und damit einer höheren Latenz führen, weshalb davon abgeraten wird.
Weitere Informationen zum Melden von Fehlern in Cloud Functions finden Sie unter Fehler melden.
Zeitlimit
Die Ausführungszeit der Funktion kann durch ein Zeitlimit begrenzt werden, das Sie beim Bereitstellen der Funktion angeben können. Eine Funktion läuft standardmäßig nach 1 Minute ab, Sie können diesen Zeitraum aber auf bis zu 9 Minuten verlängern.
Sobald die Ausführung der Funktion das Zeitlimit überschreitet, wird dem Aufrufer ein Fehlerstatus zurückgegeben. CPU-Ressourcen, die von der abgelaufenen Funktionsinstanz verwendet werden, werden gedrosselt und die Verarbeitung der Anfrage kann sofort pausiert werden. Pausierte Aufträge können bei nachfolgenden Anfragen fortgesetzt werden, was unerwartete Auswirkungen haben kann.
Das folgende Snippet enthält Code, der 2 Minuten nach dem Start der Funktionsausführung ausgeführt werden soll. Bei einem Zeitlimit von 1 Minute würde dieser Code möglicherweise niemals ausgeführt:
Node.js
Python
Go
Java
Unter bestimmten Umständen kann der obige Code erfolgreich, aber in unerwarteter Weise ausgeführt werden. Stellen Sie sich vor, was passiert, wenn die Funktion das Zeitlimit überschreitet. Die Instanz, die die Anforderung liefert, wird pausiert (durch Drosselung der CPU). Ausstehende Arbeiten werden pausiert. Wenn eine nachfolgende Anfrage an dieselbe Instanz weitergeleitet wird, wird die Arbeit fortgesetzt und Function running...
wird an Logs gesendet.
Als häufiges Symptom dieses Verhaltens scheint es dann so, dass Arbeit und Logs aus einer Anforderung in eine nachfolgende Anforderung "überlaufen". Da Sie nicht sicher davon ausgehen können, dass pausierte Arbeit fortgesetzt wird, sollten Sie sich nicht auf dieses Verhalten verlassen. Stattdessen sollte Ihre Funktion Zeitüberschreitungen durch eine Kombination der folgenden Methoden vermeiden:
- Legen Sie ein Zeitlimit fest, das über der erwarteten Ausführungszeit liegt.
- Verfolgen Sie die verbleibende Ausführungszeit und führen Sie die Bereinigung bzw. den Exit frühzeitig durch.
Nutzen Sie bei der Bereitstellung das Flag --timeout
, um die maximale Ausführungszeit einer Funktion mit dem Befehlszeilentool gcloud
festzulegen:
gcloud functions deploy FUNCTION_NAME --timeout=TIMEOUT FLAGS...
Im obigen Befehl bezieht sich FLAGS...
auf andere Optionen, die Sie während der Bereitstellung Ihrer Funktion übergeben. Eine vollständige Referenz zum deploy
-Befehl finden Sie unter gcloud functions deploy
.
Sie können das Zeitlimit auch während der Funktionserstellung in der Cloud Console festlegen:
Gehen Sie zur Cloud Functions-Übersichtsseite in der Cloud Console.
Klicken Sie auf Funktion erstellen.
Füllen Sie die erforderlichen Felder für die Funktion aus.
Klicken Sie auf Mehr, um die erweiterten Einstellungen anzuzeigen.
Geben Sie einen Wert in das Feld Zeitlimit ein.
Speicher
Verwenden Sie zum Festlegen des Speichers einer Funktion mit dem gcloud
-Befehlszeilentool das --memory
-Flag mit der Anzahl von Megabyte (128
, 256
, 512
, 1024
, 2048
, 4096
). Beispiel:
gcloud functions deploy FUNCTION_NAME --memory=MEMORY
Standardmäßig beträgt der jeder Funktion zugewiesene Speicher 256 MB.
Dateisystem
Die Funktionsausführungsumgebung enthält eine ausführbare Funktionsdatei sowie Dateien und Verzeichnisse, die im bereitgestellten Funktionspaket enthalten sind, z. B. lokale Abhängigkeiten. Diese Dateien sind in einem schreibgeschützten Verzeichnis enthalten, das basierend auf dem Speicherort der Funktionsdatei ermittelt werden kann. Beachten Sie, dass sich das Funktionsverzeichnis vom aktuellen Arbeitsverzeichnis unterscheiden kann.
Im folgenden Beispiel werden Dateien aufgelistet, die sich im Funktionsverzeichnis befinden:
Node.js
Python
Go
Java
C#
Ruby
Sie können auch Code aus anderen mit der Funktion bereitgestellten Dateien laden.
Der einzige beschreibbare Teil des Dateisystems ist das Verzeichnis /tmp
, mit dem Sie temporäre Dateien in einer Funktionsinstanz speichern können. Es handelt sich um den lokalen Datenträgerbereitstellungspunkt "tmpfs". In dieses Volume geschriebene Daten werden im Arbeitsspeicher gespeichert. Beachten Sie, dass dadurch die für die Funktion bereitgestellten Speicherressourcen belegt werden.
Der Rest des Dateisystems ist schreibgeschützt und für die Funktion zugänglich.
Netzwerk
Die Funktion kann über Standardbibliotheken der Laufzeit oder Drittanbieter auf das öffentliche Internet zugreifen. Zum Beispiel können Sie einen HTTP-Endpunkt wie folgt aufrufen:
Node.js
Python
Go
Java
C#
Ruby
Versuchen Sie, Netzwerkverbindungen in mehreren Funktionsaufrufen wiederzuverwenden, wie unter Netzwerke optimieren beschrieben. Eine Verbindung, die 2 Minuten lang nicht verwendet wurde, wird vom System möglicherweise geschlossen. Weitere Versuche, eine geschlossene Verbindung zu verwenden, führen zu einem Fehler "Verbindung zurückgesetzt". Ihr Code sollte daher entweder eine Bibliothek verwenden, die geschlossene Verbindungen verarbeiten kann, oder diese explizit behandeln, wenn Netzwerkkonstrukte auf niedriger Ebene verwendet werden.
Mehrere Funktionen
Jede bereitgestellte Funktion ist von allen anderen Funktionen isoliert, selbst wenn diese von derselben Quelldatei stammen. Sie teilen weder Arbeitsspeicher, globale Variablen, Dateisysteme noch andere Zustände.
Zum Freigeben von Daten für alle bereitgestellten Funktionen können Sie Speicherdienste wie Datastore, Firestore oder Cloud Storage verwenden. Alternativ können Sie mit den entsprechenden Triggern eine Funktion aus einer anderen aufrufen. Führen Sie zum Beispiel eine HTTP-Anfrage an den Endpunkt einer HTTP-Funktion aus oder veröffentlichen Sie eine Nachricht an ein Pub- / Sub-Thema, um eine Pub/Sub-Funktion auszulösen.