Arbeiten mit Aufgabenwarteschlangen in JavaScript (Node.js)
In modernen Webanwendungen und Microservice-Architekturen entsteht häufig die Notwendigkeit, Hintergrundaufgaben auszuführen, die den Hauptausführungsthread nicht blockieren dürfen. Aufgabenwarteschlangen sind ein leistungsstarkes Werkzeug, das bei der Verwaltung asynchroner Operationen hilft und Zuverlässigkeit, Skalierbarkeit und Fehlertoleranz gewährleistet. In diesem Artikel werden wir im Detail besprechen, wie man mit Aufgabenwarteschlangen in der Node.js-Umgebung arbeitet, die wichtigsten Bibliotheken betrachten und praktische Codebeispiele schreiben.
Was ist eine Aufgabenwarteschlange und warum wird sie benötigt?
Eine Aufgabenwarteschlange (Task Queue) ist eine Datenstruktur, die nach dem FIFO-Prinzip (First In, First Out) arbeitet. Aufgaben werden in die Warteschlange eingereiht und von Workern in der Reihenfolge ihres Eingangs verarbeitet. Die Hauptanwendungsszenarien umfassen:
- Versand von E-Mail-Benachrichtigungen — der Benutzer muss nicht warten, bis die E-Mail gesendet wurde.
- Generierung von Berichten oder PDFs — ressourcenintensive Operationen, die besser asynchron ausgeführt werden.
- Verarbeitung hochgeladener Bilder — Größenänderung, Komprimierung, Hinzufügen von Wasserzeichen.
- Integration mit externen APIs — Ausführung von Anfragen mit Ratenbegrenzung (rate limiting).
- Geplante Aufgaben (cron) — regelmäßige Bereinigung der Datenbank oder Aktualisierung des Caches.
Die Verwendung von Warteschlangen ermöglicht es, den Hauptserver zu entlasten, die Fehlertoleranz zu erhöhen (falls ein Worker ausfällt, wird die Aufgabe an einen anderen übergeben) und die Ausführung der Aufgabe auch bei vorübergehenden Ausfällen zu garantieren.
Einfache In-Memory-Warteschlange in Node.js
Zunächst implementieren wir eine sehr einfache Warteschlange ohne externe Abhängigkeiten. Dieser Ansatz eignet sich für das Prototyping oder sehr kleine Projekte, wird aber für die Produktion aufgrund fehlender Persistenz nicht empfohlen.
class SimpleQueue { constructor() { this.queue = []; this.processing = false; }
enqueue(task) { this.queue.push(task); this.processNext(); }
async processNext() { if (this.processing || this.queue.length === 0) return; this.processing = true; const task = this.queue.shift(); try { await task(); } catch (err) { console.error('Fehler bei der Aufgabenausführung:', err); } finally { this.processing = false; this.processNext(); } }}
// Beispiel zur Verwendungconst queue = new SimpleQueue();
queue.enqueue(async () => { console.log('Aufgabe 1: E-Mail senden');});
queue.enqueue(async () => { console.log('Aufgabe 2: Bericht generieren');});
console.log('Aufgaben zur Warteschlange hinzugefügt');Dieser Code demonstriert die grundlegende Mechanik: Aufgaben werden nacheinander ausgeführt. Beim Neustart des Servers gehen jedoch alle unverarbeiteten Aufgaben verloren.
Bull — professionelle Warteschlange auf Redis
Für ernsthafte Projekte in Node.js ist die Bibliothek Bull der De-facto-Standard. Sie verwendet Redis als Backend, was Persistenz, Unterstützung für Prioritäten, Wiederholungsversuche und verzögerte Aufgaben gewährleistet.
Installation:
npm install bullBeispiel eines einfachen Producers und Consumers:
const Queue = require('bull');
// Erstellen einer Warteschlange mit dem Namen 'email-queue'const emailQueue = new Queue('email-queue', { redis: { host: 'localhost', port: 6379 }});
// Worker-Funktion: Verarbeitet AufgabenemailQueue.process(async (job) => { const { to, subject, body } = job.data; console.log(`Sende E-Mail an ${to}: ${subject}`); // Hier würde der eigentliche Code zum Senden der E-Mail stehen await new Promise(resolve => setTimeout(resolve, 1000)); return { success: true };});
// Hinzufügen einer Aufg