Promise-Warteschlangen und Batch-Verarbeitung gleichzeitiger Aufgaben in Deno
25. September 2024
0 Min. LesezeitWas ist Deno?
Deno ist eine sichere Laufzeitumgebung für JavaScript und TypeScript. Sie basiert auf derselben V8-JavaScript-Engine wie Node.js, bietet aber wesentliche Verbesserungen. Entwickelt wurde Deno von Ryan Dahl, dem ursprünglichen Erfinder von Node.js, um einige der zentralen Probleme und Einschränkungen zu beheben, die ihm im Laufe der Zeit bei Node.js aufgefallen waren. Deno soll die Sicherheit erhöhen, die Verwaltung von Modulen vereinfachen und TypeScript direkt unterstützen.
Sie sehen, wie der Server mit der Zeichenfolge "Hello World" auf Anfragen antwortet. Die Anweisung import ruft die Methode serve direkt über eine URL aus dem HTTP-Server-Modul der Deno-Standardbibliothek ab. Das ist eine der Besonderheiten von Deno: Module werden von Web-URLs abgerufen und zwischengespeichert. Dadurch ist kein Paketmanager wie npm erforderlich.
Gleichzeitige Aufgaben in Deno verarbeiten
Die Verarbeitung gleichzeitiger Aufgaben ist für jedes moderne Web-Framework und jede moderne Laufzeitumgebung wie Deno entscheidend. In JavaScript sind asynchrone Vorgänge üblich – etwa das Abrufen von Daten aus einer Datenbank, das Senden von HTTP-Anfragen oder das Lesen von Dateien von der Festplatte.
Da JavaScript single-threaded ist, nutzt es seine ereignisgesteuerte Funktionsweise, um diese Aufgaben gleichzeitig zu verwalten, ohne den Hauptthread zu blockieren.
Das obige Beispiel zeigt, wie JavaScript mit Promise.all() gleichzeitige Aufgaben verarbeitet. Bei der gleichzeitigen Ausführung mehrerer Promises wartet Promise.all(), bis alle abgeschlossen sind, und gibt dann ihre Ergebnisse zurück. Das ist gut für die Leistungsoptimierung, hat aber auch Nachteile. Schlägt ein Promise fehl, werden auch alle anderen abgelehnt. Bei einer großen Anzahl von Aufgaben kann das zu einem hohen Speicherverbrauch führen.
In Deno können wir Promise-Warteschlangen und Batch-Verarbeitung von Aufgaben nutzen, um die Nebenläufigkeit effizienter zu verwalten. So lassen sich viele Aufgaben besser verarbeiten, ohne eine Speicherüberlastung oder einen vollständigen Ausfall bei einem einzelnen Aufgabenfehler zu riskieren.
Sehen wir uns genauer an, wie diese Warteschlangen- und Batch-Verarbeitungstechniken Ihnen helfen, effizienteren, robusteren und leistungsfähigeren Code zu schreiben.
Promise-Warteschlangen und gleichzeitige Aufgaben verstehen
Promise-Warteschlangen sind ein wesentlicher Bestandteil der asynchronen Programmierung in JavaScript. Wenn Sie Promise-Warteschlangen und Nebenläufigkeit verstehen, können Sie Effizienz und Leistung Ihrer Anwendungen deutlich steigern.
Ein sequenzieller Ansatz für asynchrone Aufgaben
Eine Promise-Warteschlange ist eine Liste von Promise-Objekten, die darauf warten, erfüllt oder abgelehnt zu werden. Ein „Promise“ in JavaScript ist ein Objekt, das den späteren Abschluss oder Fehlschlag eines asynchronen Vorgangs repräsentiert.
Eine Promise-Warteschlange hilft dabei, mehrere Promises zu verwalten, die in einer bestimmten Reihenfolge ausgeführt werden müssen. Sie stellt sicher, dass Promises nacheinander erfüllt werden, und verhindert so potenzielle Probleme durch die Ausführung asynchronen Codes.
Hier sehen Sie ein Beispiel für eine einfache Promise-Warteschlange:
Im obigen Beispiel nimmt Promise.all ein iterierbares Objekt mit Promises entgegen und gibt ein neues Promise zurück, das mit einem Array der erfüllten Werte aus den übergebenen Promises in derselben Reihenfolge erfüllt wird. Wird ein Promise abgelehnt, wird auch das zurückgegebene Promise mit dem Grund für die Ablehnung des ersten abgelehnten Promise abgelehnt.
Was bedeutet Nebenläufigkeit in JavaScript?
Im Kontext von JavaScript bezeichnet Nebenläufigkeit die Fähigkeit der JavaScript-Engine, mehrere Codeabschnitte (Aufgaben) auszuführen, ohne die Ausführung anderer Aufgaben zu blockieren. Dabei ist zu beachten, dass JavaScript weitgehend single-threaded ist, also im Userspace-Code jeweils nur einen Vorgang ausführen kann. Mithilfe asynchroner Programmierung (etwa mit Promises) kann JavaScript Aufgaben jedoch so planen, dass sie gleichzeitig ausgeführt werden.
Nebenläufigkeit in JavaScript kann die Leistung erheblich verbessern, insbesondere bei E/A-Vorgängen wie Netzwerkanfragen, Dateisystemoperationen oder anderen Vorgängen, bei denen eine gewisse Wartezeit anfällt.
Hier sehen Sie ein Beispiel für gleichzeitige Aufgaben in JavaScript:
In diesem Beispiel werden task1 und task2 gleichzeitig ausgeführt. Obwohl task1 länger dauert, blockiert die Aufgabe die Ausführung von task2 nicht.
Dieses Codebeispiel ähnelt möglicherweise dem vorherigen. In den vorherigen JavaScript-Codebeispielen haben wir jedoch Promise.resolve(1) verwendet, das sofort mit der Ganzzahl als Rückgabewert erfüllt wurde. Dieses Beispiel für gleichzeitige Ausführung zeigt, wie Aufgaben für die gleichzeitige Ausführung in einem Hintergrundthread geplant werden können.
Praxisbeispiele für den Einsatz von Promise-Warteschlangen und Nebenläufigkeit
Promise-Warteschlangen und Nebenläufigkeit kommen überall in JavaScript zum Einsatz. In einer Webanwendung müssen Sie beispielsweise möglicherweise Daten gleichzeitig von mehreren Endpunkten abrufen und anschließend in einer bestimmten Reihenfolge verarbeiten – ein gutes Beispiel für den Einsatz von Promise-Warteschlangen und Nebenläufigkeit.
Stellen Sie sich ein Online-System für den Kauf von Kinokarten vor. Wenn eine Person eine Buchung vornimmt, muss das System möglicherweise mehrere Aufgaben ausführen: die Sitzplatzverfügbarkeit prüfen, den Sitzplatz reservieren, die Zahlung abwickeln und eine Bestätigungs-E-Mail senden. Mit Promise-Warteschlangen lassen sich diese Aufgaben in der richtigen Reihenfolge ausführen. Aufgaben wie das gleichzeitige Senden von Bestätigungs-E-Mails an mehrere Personen können parallel erledigt werden, um die Effizienz zu steigern.
Wenn Sie die Konzepte von Promise-Warteschlangen und Nebenläufigkeit in JavaScript verstehen und anwenden, können Sie Leistung und Effizienz Ihrer Anwendungen deutlich steigern. Bei Ihrer weiteren Beschäftigung mit JavaScript werden diese Konzepte immer nützlicher, wenn Sie komplexe, leistungsstarke Anwendungen entwickeln.
Eine Deno-Anwendung für HTTP-Fetch-Anfragen erstellen
Sehen wir uns an, wie Sie eine einfache Deno-Anwendung erstellen, die mehrere HTTP-Fetch-Anfragen gebündelt senden kann. Dazu verwenden wir die Methode Promise.allSettled(). Unsere Anwendung ruft Paketdaten über den JSON-Endpunkt der npm-Registry ab.
Wir empfehlen Ihnen, dieser Schritt-für-Schritt-Anleitung zu folgen. Wenn Sie jedoch den vollständigen Quellcode benötigen, um die im Artikel beschriebenen Details der Deno-Anwendung nachzuvollziehen, finden Sie ihn in diesem GitHub-Repository: https://github.com/snyk-snippets/deno-promise-queues-batch-queue
Die Deno-Umgebung einrichten
Der erste Schritt beim Erstellen unserer Anwendung ist die Einrichtung der Deno-Umgebung. Deno ist eine sichere JavaScript- und TypeScript-Laufzeitumgebung, die auf der V8-JavaScript-Engine und der Programmiersprache Rust basiert. Um Deno zu installieren, führen Sie folgenden Befehl aus:
Prüfen Sie nach Abschluss der Installation mit folgendem Befehl, ob die Installation erfolgreich war:
Dieser Befehl gibt die installierte Deno-Version aus.
Ein einfaches Deno-Programm schreiben
Unser Deno-Programm sendet mithilfe der Methode Promise.allSettled() mehrere Fetch-Anfragen gebündelt.
Promise.allSettled() ist eine Methode, die ein Promise zurückgibt. Dieses wird aufgelöst, sobald alle angegebenen Promises entweder erfüllt oder abgelehnt wurden. Zurückgegeben wird ein Array mit Objekten, die jeweils das Ergebnis eines Promises beschreiben. Die Methode ist besonders nützlich, wenn die Reihenfolge, in der die Promises abgeschlossen werden, keine Rolle spielt und Sie lediglich wissen müssen, wann alle abgeschlossen sind.
Was ist der Unterschied zwischen Promise.all() und Promise.allSettled()?
Promise.all() eignet sich, wenn alle Promises erfolgreich sein müssen und bei einer Ablehnung sofort ein Fehler auftreten soll. Promise.allSettled() ist dagegen nützlich, wenn Sie unabhängig vom Ergebnis warten möchten, bis alle Promises abgeschlossen sind.
Paketdaten über den JSON-Endpunkt der npm-Registry abrufen
Unsere Anwendung ruft Paketdaten über den JSON-Endpunkt der npm-Registry ab. Die npm-Registry ist eine umfangreiche Datenbank mit JavaScript-Paketen. Sie bietet eine öffentliche API, über die wir Paketdaten im JSON-Format abrufen können. Das ist ideal für unsere Zwecke, da sich die abgerufenen Daten so einfach verarbeiten lassen.
Codebeispiel für das Deno-Programm
Hier sehen Sie ein Beispiel dafür, wie Sie ein Deno-Programm schreiben, das mithilfe von Promise.allSettled() mehrere HTTP-Fetch-Anfragen gebündelt sendet.
In diesem Beispiel erstellen wir ein Array mit den URLs, die wir abrufen möchten. Anschließend durchlaufen wir dieses Array und senden für jede URL eine Fetch-Anfrage. Mit Promise.allSettled() warten wir, bis alle Fetch-Anfragen abgeschlossen sind, und verarbeiten dann die Ergebnisse.
Eine Teilausgabe des obigen Deno-Programms:
Die Deno-Anwendung mit dem BatchQueue-Modul optimieren
Das BatchQueue-Modul in Deno bietet einen Mechanismus, mit dem Sie die Ausführung von Aufgaben in Ihrer Deno-Anwendung verwalten und steuern können. Dieses leistungsstarke Tool begrenzt die Anzahl gleichzeitig ausgeführter Aufgaben. Das ist nützlich, wenn Sie mit Ressourcen arbeiten, deren Nutzung beschränkt ist, oder wenn Aufgaben viel CPU-Leistung oder Speicher benötigen.
Insbesondere bei HTTP-Anfragen ist es ratsam, verantwortungsvoll vorzugehen und die Anzahl gleichzeitiger Anfragen an einen externen Dienst wie die npm-Registry zu begrenzen, damit dieser nicht ausgelastet oder überlastet wird. Eine Begrenzung gleichzeitiger Anfragen hilft Ihrem Programm außerdem, nicht als potenzieller Denial-of-Service-Angriff eingestuft zu werden.
Mit BatchQueue in Deno können Sie eine Promise-Warteschlange erstellen und die Promises in Stapeln ausführen. Diese Technik kann die Leistung Ihrer Deno-Anwendung erheblich verbessern, indem sie eine Überlastung Ihrer Systemressourcen verhindert und den Durchsatz der Aufgaben erhöht.
Das Deno-Programm durch eine Warteschlange mit begrenzter Nebenläufigkeit optimieren
Betrachten wir ein Deno-Programm, das mehrere HTTP-Anfragen gleichzeitig sendet. Standardmäßig versucht Deno, alle Aufgaben gleichzeitig auszuführen. Das kann jedoch problematisch werden, wenn die Anzahl der Aufgaben so groß ist, dass alle verfügbaren Systemressourcen beansprucht werden, oder wenn die Aufgaben mit externen Systemen interagieren, für die Nutzungsbeschränkungen gelten.
Mit dem BatchQueue-Modul können wir die Anzahl gleichzeitiger Aufgaben steuern. Hier sehen Sie ein vereinfachtes Codebeispiel für die Verwendung von BatchQueue in einem Deno-Programm:
In diesem Beispiel ist für BatchQueue eine concurrency von 5 festgelegt. Das bedeutet, dass nur fünf Aufgaben gleichzeitig ausgeführt werden können.
Jede Aufgabe wird in die BatchQueue eingereiht. Die Funktion makeHttpRequest wird aufgerufen, sobald die Aufgabe zur Ausführung aus der Warteschlange entnommen wird. BatchQueue stellt sicher, dass nie mehr als fünf Aufgaben gleichzeitig ausgeführt werden.
Promise-Warteschlange im Vergleich zu Promise.all()
Wenn Sie das optimierte Deno-Programm mit BatchQueue mit der ursprünglichen Version vergleichen, werden Sie feststellen, dass sich der Verbrauch der Systemressourcen und die Gesamtleistung Ihrer Deno-Anwendung deutlich verbessert haben.
Der BatchQueue-Mechanismus stellt sicher, dass das Deno-Programm nicht alle verfügbaren Systemressourcen aufbraucht – ein häufiges Problem bei der gleichzeitigen Ausführung einer großen Anzahl von Aufgaben.
Mit dem BatchQueue-Modul in Deno können Sie die Ausführung von Aufgaben in Ihrer Anwendung effizient verwalten und Leistung sowie Reaktionsfähigkeit Ihrer Deno-Programme verbessern.
Eine ProgressBar zur Deno-Anwendung hinzufügen
Feedback in Ihrer Deno-Anwendung kann die Benutzererfahrung erheblich verbessern. Eine der intuitivsten Möglichkeiten, Feedback in einer CLI bereitzustellen, ist eine Fortschrittsanzeige.
Ein solches Modul ist ProgressBar. Damit lässt sich eine Fortschrittsanzeige ganz einfach zu Ihrer Deno-Anwendung hinzufügen.
Das ProgressBar-Modul ist ein schlankes und umfassend anpassbares Paket, mit dem sich ganz einfach eine Fortschrittsanzeige in Ihre Anwendungen integrieren lässt. Es bietet eine unkomplizierte API für eine einfache Implementierung und Anpassungsmöglichkeiten, mit denen Sie das Aussehen an Ihre Anwendung anpassen können.
Um das ProgressBar-Modul zu installieren, importieren Sie es wie folgt in Ihre Anwendung:
Mit ProgressBar den Fortschritt abgeschlossener Anfragen anzeigen
Nach dem Import des ProgressBar-Moduls können Sie damit den Fortschritt abgeschlossener Anfragen in Ihrer Deno-Anwendung visuell darstellen.
Erstellen Sie eine neue ProgressBar-Instanz und geben Sie die Gesamtzahl der Schritte an, die die Fortschrittsanzeige darstellen soll. Rufen Sie anschließend für jede abgeschlossene Anfrage die Methode .tick() auf, um die Anzeige weiterzuschalten.
Hier sehen Sie ein einfaches Beispiel dafür, wie Sie mit ProgressBar den Fortschritt abgeschlossener Anfragen verfolgen und ausgeben:
In diesem Beispiel wird der Fortschrittsbalken bei jeder abgeschlossenen Anfrage um einen Schritt weitergeschaltet. Das Erscheinungsbild des Fortschrittsbalkens lässt sich anpassen, indem Sie beim Erstellen der ProgressBar-Instanz die Optionen complete und incomplete ändern.
Codebeispiel des Deno-Programms mit ProgressBar
Sehen wir uns zum Schluss ein ausführlicheres Beispiel an, in dem ProgressBar in einem praxisnahen Szenario zum Einsatz kommt. Angenommen, wir müssen eine Reihe von API-Anfragen senden und möchten ihren Fortschritt nachverfolgen.
In diesem Beispiel simulieren wir das Senden von API-Anfragen und verwenden ProgressBar, um ihren Fortschritt nachzuverfolgen. Jeder Aufruf von sendRequest() steht für eine gesendete Anfrage, auf deren Antwort gewartet wird. Bei jeder abgeschlossenen Anfrage wird der Fortschrittsbalken mit dem Aufruf von progressBar.tick() weitergeschaltet.
Wenn wir unser vollständiges Deno-Programm mit den Modulen Promises Batch Queue und ProgressBar ausführen, erhalten wir folgende Befehlszeilenausgabe:
Und schon ist es geschafft! Sie haben Ihrer Deno-Anwendung jetzt eine ProgressBar hinzugefügt, die den Fortschritt der abgeschlossenen Anfragen anzeigt. Das ProgressBar-Modul ist eine wertvolle Ergänzung für Ihre Deno-Anwendungen und bietet eine intuitive Möglichkeit, den Status lang laufender Aufgaben nachzuverfolgen. Probieren Sie es in Ihrem nächsten Deno-Projekt aus!
Ein vollständiges Deno-Anwendungsbeispiel für diese Promise Batch Queue finden Sie unter: https://github.com/snyk-snippets/deno-promise-queues-batch-queue
Sicherheit in Deno-Anwendungen – SSRF-Schwachstelle
Cybersicherheit ist ein grundlegender Aspekt jedes Softwareentwicklungsprojekts. Im Deno-Ökosystem müssen Entwickler auf ein potenzielles Sicherheitsrisiko besonders achten: die Server-Side Request Forgery (SSRF)-Schwachstelle.
In diesem Abschnitt erfahren Sie mehr über SSRF-Schwachstellen, die Risiken, die sie für Deno-Anwendungen darstellen, und Möglichkeiten, sie einzudämmen. Außerdem zeigen wir ein praktisches Codebeispiel für ein abgesichertes Deno-Programm.
Was ist eine SSRF-Schwachstelle?
SSRF ist eine Schwachstelle, durch die ein Angreifer eine serverseitige Anwendung dazu bringen kann, nicht autorisierte Netzwerkinteraktionen auszuführen. Das ist riskant, weil Angreifer dadurch mit internen Systemen interagieren können, was möglicherweise zu unbefugtem Zugriff oder Datenlecks führt. Im Grunde kann eine SSRF-Schwachstelle den Server zum Proxy für böswillige Aktivitäten des Angreifers machen.
SSRF-Schwachstellen in Deno-Anwendungen
Deno ist standardmäßig sicher konzipiert. Das bedeutet, dass alle Skripte in einer sicheren Sandbox ausgeführt werden, sofern ihnen nicht ausdrücklich Berechtigungen erteilt wurden. Werden die erforderlichen Berechtigungen jedoch gewährt, können Deno-Anwendungen für SSRF-Schwachstellen anfällig sein.
Wird einer Deno-Anwendung beispielsweise die Berechtigung „allow-net“ erteilt, kann sie Netzwerkanfragen an beliebige Hosts senden. Ein Angreifer könnte dies ausnutzen, indem er einen bösartigen Host in die Anfrage einschleust und so eine SSRF-Schwachstelle auslöst.
Entwickler führen Deno-Anwendungen wie das von uns erstellte Promise-Queue-Programm häufig folgendermaßen aus:
SSRF-Schwachstellen in Deno-Anwendungen eindämmen
Um potenzielle SSRF-Schwachstellen einzudämmen, sollten Entwickler sichere Programmierpraktiken anwenden, darunter:
Minimale Berechtigungen: Erteilen Sie nur die Berechtigungen, die für den ordnungsgemäßen Betrieb der Deno-Anwendung erforderlich sind. Wenn die Anwendung keine Netzwerkanfragen senden muss, erteilen Sie ihr nicht die Berechtigung
--allow-net(oder das entsprechende Kurzformat-Aals Befehlszeilen-Flag).Eingabevalidierung: Validieren Sie alle Benutzereingaben sorgfältig und weisen Sie verdächtige oder fehlerhafte Eingaben zurück. Nutzer dürfen in der Regel keine absoluten URLs angeben. Beschränken Sie sie auf relative URLs, die Ihre Anwendung hostet und zulässt.
Codebeispiel für ein abgesichertes Deno-Programm
Sehen wir uns eine einfache Deno-Anwendung mit Sicherheitsmaßnahmen an, die SSRF-Schwachstellen eindämmen.
In diesem Codebeispiel stellen wir einen HTTP-Server auf Port 8000 bereit. Sobald eine Anfrage eingeht, prüfen wir den Hostnamen in der Anfrage-URL. Lautet er nicht „localhost“ oder „127.0.0.1“, antworten wir mit der Meldung „Unauthorized host“ und verhindern so Anfragen an externe Hosts.
Durch sichere Programmierpraktiken und einen sorgfältigen Umgang mit den Berechtigungen für unsere Deno-Anwendungen können wir das Risiko von SSRF-Schwachstellen wirksam eindämmen.
Warum die effiziente Verarbeitung gleichzeitiger Aufgaben in Deno wichtig ist
Die effiziente Verwaltung gleichzeitiger Aufgaben ist entscheidend für die Entwicklung leistungsstarker Anwendungen. In Deno ist sie besonders wichtig, da Deno für modernen JavaScript- und TypeScript-Code entwickelt wurde, der häufig viele asynchrone Vorgänge umfasst.
Mithilfe von Promise-Queues und Batchverarbeitung können wir Ressourcen effizient verwalten und Probleme wie Speicherüberläufe oder eine Drosselung der CPU vermeiden. Das verbessert Zuverlässigkeit und Reaktionsfähigkeit der Anwendung und sorgt für eine bessere Nutzererfahrung.
Deno und seine Module weiter erkunden
Deno bietet weit mehr als Promise-Queues und die Batchverarbeitung gleichzeitiger Aufgaben. Die leistungsstarke und flexible Laufzeitumgebung umfasst zahlreiche Funktionen und Module, mit denen sich robuste, sichere und effiziente Anwendungen entwickeln lassen.
Die Standardbibliothek von Deno enthält beispielsweise hochwertige, geprüfte Module, deren Kompatibilität mit Deno gewährleistet ist. Dazu gehören Module für die Datums- und Zeitverarbeitung, Hashing, HTTP-Server, Tests und vieles mehr.
Dank des starken Sicherheitsfokus ermöglicht Deno außerdem eine präzise Kontrolle darüber, worauf eine Anwendung zugreifen kann – ein entscheidender Vorteil angesichts der heutigen Cybersicherheitsbedrohungen. Dennoch erfordern viele Schwachstellen, etwa SSRF-Schwachstellen, weiterhin gezielte Eingriffe und zusätzliche Sicherheitsmaßnahmen durch Entwickler.
Wenn Sie mehr über Deno und seine Verwendung erfahren möchten, lesen Sie die offizielle Deno-Dokumentation. Dort finden Sie Informationen zu verschiedenen Deno-Funktionen und -Modulen, die Ihnen dabei helfen, Promise-Queues und die Batchverarbeitung gleichzeitiger Aufgaben zu verstehen.
Damit Ihre Anwendung sicher ist, müssen Sie verschiedene Bedrohungen für die Anwendungssicherheit kennen, darunter Server-Side Request Forgery (SSRF)-Schwachstellen. Die folgenden Ressourcen helfen Ihnen dabei, SSRF-Schwachstellen zu verstehen und einzudämmen:
URL-Verwirrungsschwachstellen in freier Wildbahn: Parser-Inkonsistenzen im Fokus
Sichere JavaScript-URL-Validierung- Eine technische Analyse des Cloud-Konfigurationsverstoßes bei Capital One
Sichern Sie Ihren Code während der Entwicklung
Snyk scannt Ihren Code auf Qualitäts- und Sicherheitsprobleme und gibt Ihnen direkt in Ihrer IDE Empfehlungen zur Behebung.
