Jürgen Fälchle - stock.adobe.c

So optimiert man AWS Lambda-Funktionen

Gleichzeitigkeitsgrenzen und Kaltstarts beeinflussen die Leistung von Lambda-Funktionen. Wenn man die Servicefunktionen kennt, lassen sich Verzögerungen vermeiden.

AWS Lambda-Funktionen sind ideal dafür geeignet, einfache Aufgaben auszuführen, die weniger als fünf Minuten dauern. Der Vorteil: Sie müssen dafür keine Server verwalten. Das bedeutet allerdings auch, dass bei jeder Ausführung einer Funktion nicht garantiert werden kann, dass dies auf der gleichen Hardware oder virtuellen Instanz erfolgt.

Dies macht es schwierig, Dinge wie Cache Requests für Leistungsverbesserungen lokal auszuführen – es sei denn, dieser Cache ist für denselben individuellen Request. Die Frage ist: Wie genau arbeiten diese Funktionen?

Die Arbeitsweise von AWS Lambda-Funktionen zu kennen, ist nur von Vorteil: Sobald man den AWS Lambda Lifecycle kennt, lassen sich bessere Entscheidungen bei der Erstellung und Verwaltung von Funktionen treffen.

Wenn Sie Lambda-Funktionen einsetzen, folgen sie einem Spin-Up-Prozess – der Ihnen nicht berechnet wird – bevor sie den transaktionsspezifischen Code ausführen. Den sollten Sie natürlich kennen, es ist aber auch wichtig, andere Funktionen zu verstehen. Dazu gehört auch, zu wissen, dass die Funktionen in einem Container ausgeführt werden, um Ihren Code von den Funktionen anderer Benutzer zu isolieren, die auf derselben Hardware laufen.

Verkürzen Sie Ihren Kaltstart. Hier ein Blick auf den AWS Lambda Lifecycle:

Der AWS Lambda Lifecycle. AWS berechnet den Entwicklern nur den grünen Abschnitt Execute Function - also die Ausführung der Funktion.
Abbildung 1: Der AWS Lambda Lifecycle. AWS berechnet den Entwicklern nur den grünen Abschnitt Execute Function - also die Ausführung der Funktion.

Zuerst führen Lambda-Funktionen den Bootstrapping-Code für die Sprache aus und dann den Anwendungscode – das passiert alles innerhalb der Kaltstartphase. Lambda „weiß“, dass es eine gewisse Bootstrapping-Zeit geben wird, so dass es die Funktionscontainer nach Ende der ersten Ausführung wiederverwenden kann. Container frieren nach Beendigung der Ausführung so lange ein, bis sie später wieder verwendet werden.

Wenn die Anwendung bereit ist, Verbindungen anzunehmen, startet Ihr Timer. Dies bedeutet, dass jeder Code außerhalb Ihrer Handler-Funktion – der Einstiegspunkt, der als Antwort auf ein Ereignis aufgerufen wird – kostenlos ausgeführt werden kann. Das bedeutet aber auch, dass Code für jeden neuen Kaltstart ausgeführt werden muss, bevor Ihre Funktion Anfragen entgegennehmen kann.

Der einfachste Weg, um zu sehen, wie dieses Element des AWS Lambda-Lebenszyklus funktioniert, ist, eine einfache Lambda-Funktion mit einer Gleichzeitigkeitsgrenze („Concurrency Limit“) von 1 zu schreiben. Damit kann zu einer bestimmten Zeit nur eine Instanz laufen. Setzen Sie dann konstante Befehle für die Ausführung dieser Funktion mit einer Zählervariablen außerhalb der Handler-Funktion ab:

var counter = 0;

exports.handler = async (event) => {

    counter++;

    console.log('Counter', counter);

    return `Counter says ${counter}`;

};

Nachdem Sie diesen Code in die Lambda-Konsole eingegeben und die Gleichzeitigkeitsgrenze auf eins gesetzt haben, machen Sie einige Testläufe. Sie werden feststellen, dass mit jeder Ausführung der Lambda-Funktion der Zähler erhöht wird – obwohl bei jeder Ausführung neue Anforderungen gestellt wurden. Dies liegt daran, dass die Lambda-Funktion den Container nach Ende der Ausführung für eine gewisse Zeit einfriert und denselben Container dann für die nächste Ausführung wiederverwendet.

Die Zählervariable wird außerhalb der Handler-Funktion gespeichert, so dass sie Teil des gespeicherten Zustandes ist, der in die nächste Ausführung zurückgeladen wird. Warten Sie eine Stunde oder speichern Sie eine neue Version Ihrer Funktion, und versuchen Sie es dann erneut. Sie werden feststellen, dass der Zähler bei der nächsten Ausführung wieder auf 1 zurückgestellt wird, nachdem die Lambda-Funktion beendet wurde. Der Grund: im eingefrorenen Lambda-Container wurde eine Garbage Collection ausgeführt und es wurde stattdessen ein neuer gebaut.

Während es für Ihre Funktionen wichtig ist, eine kurze Kaltstartzeit zu haben, sollten Sie auch Caching nutzen. Außerdem sollten Sie alle allgemeinen Aufgaben außerhalb Ihres Haupt-Handlers ausführen, da diese Operationen nur einmal pro gleichzeitiger Ausführung stattfinden.

Niedriger Gleichzeitigkeitslimits können Performance verbessern

Entgegen der Erwartetung kann es bei Reduzierung der Gleichzeitigkeit zu einer Leistungssteigerung bei bestimmten Funktionen kommen. Wenn Ihre Funktion zum Beispiel 30 Sekunden für den Start, aber nur eine Sekunde für die tatsächliche Ausführung benötigt, würde die erste Ausführung 31 Sekunden dauern, die zweite jedoch nur eine Sekunde. Wenn eine Anfrage 15 Sekunden später kommt, würde die dritte Ausführung immer noch 31 Sekunden dauern.

Wenn Sie aber die Gleichzeitigkeitsstufe auf eins setzen, würde die zweite Ausführung nur 17 Sekunden dauern, da sie auf das Ende des ersten Containers warten und dann den gleichen Container wiederverwenden würde. Dieses Timing-Diagramm stellt die verschiedenen Funktionszeiten dar:

Setzen Sie ein Gleichzeitigkeitslevel oder verwenden Sie Container wieder, um die Lambda-Ausführungszeit zu reduzieren.
Abbildung 2: Setzen Sie ein Gleichzeitigkeitslevel oder verwenden Sie Container wieder, um die Lambda-Ausführungszeit zu reduzieren.

Wie Sie sehen, ist es sinnvoll, für bestimmte Szenarien Container wiederzuverwenden, anstatt Tasks gleichzeitig auszuführen. Diese Art von Anfrage ist jedoch nicht sehr gut skalierbar. Daher sollten Sie die Gleichzeitigkeit nur für Tasks senken, die lange Zeit zum Booten benötigen, aber nicht oft mehrere Anfragen gleichzeitig haben.

Planen Sie Lambda Gleichzeitigkeit mit DynamoDB-Durchsatz

Es ist üblich, Lambda-Funktionen zusammen mit DynamoDB oder einer anderen Datenbank mit begrenztem Durchsatz zu verwenden. Lambda-Funktionen werden nach Ausführungszeit abgerechnet, was bedeutet, dass Sie während des Wartens auf die Antwort von DynamoDB bezahlen müssen. Ein Beispiel: Wenn Ihre DynamoDB-Tabellenkapazität so eingestellt ist, dass fünf gleichzeitige Schreibvorgänge möglich sind, Sie aber 20 gleichzeitige Lambda-Funktionen haben, die versuchen, in diese Tabelle zu schreiben, könnten 15 dieser Funktionen stecken bleiben und müssen auf Schreibkapazität warten. Sie sollten stattdessen Ihre Lambda-Gleichzeitigkeit auf den gleichen Wert beschränken, den Ihre DynamoDB-Tabelle verarbeiten kann. So lässt es sich vermeiden, dass Sie auf DynamoDB warten müssen, um für neue Anfragen bereit zu sein.

Außerdem ist es wichtig, Alerts einzurichten, damit Sie bei vielen gedrosselten Lambda-Anfragen Ihre DynamoDB- und Gleichzeitigkeitslimits erhöhen können. Aktivieren Sie deshalb am besten AWS X-Ray, um Engpässe innerhalb von Lambda zu identifizieren. So können Sie feststellen, wie viele gleichzeitige Anfragen Ihre Lambda-Funktion bearbeiten kann, bevor Sie die Durchsatzkapazität Ihrer Datenbank erhöhen müssen.

Funktionen „vorwärmen“

Lambda skaliert automatisch, so dass Sie nicht für die Zeit ohne Requests bezahlen müssen. Wenn Ihre Funktion jedoch lange braucht, um den ersten Request zu laden oder auszuführen, kann es helfen, die Funktion „vorzuwärmen“.

Betrachten wir als Beispiel eine Funktion, die Schlüsselwörter einer Kategorie zuordnet – zum Beispiel eine, die die Phrase das ist Spam der Kategorie Spam zuordnet. Bei einer großen Anzahl von Schlüsselwörtern kann es eine Weile dauern, sie alle in den Speicher zu laden. Dies dürfte dann wahrscheinlich der zeitlich längste Teil der Anfrage sein.

Nehmen wir an, unsere Funktion sieht so aus:

# Pre-Cache all the markers

marker_cache = load_marker_cache()

 

def lambda_handler(event, context):

    # Return immediately if we're just warming

    if event.warm:

        return ''

    return categorize(event.text)

 

Wir würden dann eine CloudWatch-Ereignisregel erstellen, die stündlich mit dieser vordefinierten Eingabe ausgeführt wird:

{ warm: true }

Erstellen Sie eine CloudWatch Events-Regel für Lambda.
Abbildung 3: Erstellen Sie eine CloudWatch Events-Regel für Lambda.

Wenn die Funktion das Event warm erkennt, gibt sie einfach einen leeren String zurück, anstatt die komplexe Kategorisierungsfunktion auszuführen. Dies hält die Funktion effektiv warm und bereit für eine neue Anfrage – ohne dass etwas Unnötiges laufen muss. Die Funktion sollte den Wert sofort liefern, was bedeutet, dass Ihnen nicht mehr als eine Sekunde Lambda-Nutzung pro Stunde berechnet wird – natürlich mit Ausnahme des ersten Mals, wenn Sie den Cache erstellen.

Stellen Sie sicher, dass die Reserve Concurrency auf eins gesetzt ist, so dass nur eine Version dieser Funktion benötigt wird und gecacht bleibt.

Am Ende sind Lambda-Funktionen, auch wenn es vielleicht auf den ersten Blick den Anschein hat, keine Magie. Entwickler müssen nur den AWS Lambda-Lebenszyklus verstehen, um Funktionen zu generieren, die mit der Nachfrage skalieren können. Und manchmal ist es einfach besser, die Benutzer etwas länger warten zu lassen, als eine Datenbank abzuschalten, die versucht, Tausende von Anfragen gleichzeitig zu bearbeiten.

Folgen Sie SearchEnterpriseSoftware.de auch auf Twitter, Google+, Xing und Facebook!

Nächste Schritte

AWS Batch statt Lambda für bestimmte Workloads verwenden.

Kostenloser E-Guide: Die Serverless-Plattform AWS Lambda.

Wie löst man mit Amazon SQS Funktionen in AWS Lambda aus?

Erfahren Sie mehr über Cloud Computing