Aufgabenwarteschlangen für Go (Golang): Überblick und Vergleich der Werkzeuge
In der modernen Webentwicklung und bei der Erstellung hochlastiger Dienste in Go (Golang) entsteht fast immer die Notwendigkeit, Aufgaben asynchron, außerhalb des Hauptverarbeitungsflusses von HTTP-Anfragen, auszuführen. Das Versenden von E-Mail-Benachrichtigungen, das Generieren von PDF-Berichten, die Bildverarbeitung, die Interaktion mit externen APIs – all dies erfordert die Auslagerung in Hintergrundprozesse. Hier kommen Aufgabenwarteschlangen (Task Queues) ins Spiel.
Eine Aufgabenwarteschlange ist ein Mechanismus, der es ermöglicht, die Ausführung einer Operation zu verschieben, ihre erneute Verarbeitung im Fehlerfall zu garantieren und die Last zwischen mehreren Workern auszugleichen. Im Go-Ökosystem gibt es mehrere ausgereifte und aktiv entwickelte Bibliotheken zur Lösung dieser Aufgabe. In diesem Artikel werden wir die drei beliebtesten Ansätze im Detail untersuchen: Asynq, Machinery und die direkte Arbeit mit Redis Streams.
1. Asynq: Einfachheit und Leistungsfähigkeit auf Basis von Redis
Asynq vom Entwickler hibiken ist derzeit wohl die beliebteste Bibliothek für Aufgabenwarteschlangen in Go. Sie ist auf Redis aufgebaut und bietet einen umfangreichen Funktionsumfang „aus einer Hand“.
Wesentliche Merkmale von Asynq:
- Monitoring: Integrierte Weboberfläche (Asynqmon) zur Verfolgung von Warteschlangen, Aufgabenausführung und Fehlern.
- Planung: Möglichkeit, die Ausführung einer Aufgabe auf einen bestimmten Zeitpunkt zu verschieben (Cron-ähnliche Syntax).
- Prioritäten: Unterstützung mehrerer Warteschlangen mit unterschiedlichen Prioritäten.
- Retry und Dead Letters: Automatische Wiederholungsversuche und eine Warteschlange für „tote“ Nachrichten für nicht ausführbare Aufgaben.
- Middleware: Unterstützung von Zwischensoftware für Protokollierung, Metriken usw.
Anwendungsbeispiel für Asynq:
package main
import ( "context" "log" "time" "github.com/hibiken/asynq")
// Definieren des Aufgabentypsconst TypeEmailDelivery = "email:deliver"
// Client zum Senden von Aufgabenfunc main() { client := asynq.NewClient(asynq.RedisClientOpt{Addr: "localhost:6379"}) defer client.Close()
// Aufgabe erstellen task, err := asynq.NewTask(TypeEmailDelivery, []byte(`{"user_id": 42, "template": "welcome"}`)) if err != nil { log.Fatal(err) }
// Aufgabe mit einer Verzögerung von 5 Minuten senden info, err := client.Enqueue(task, asynq.ProcessIn(5*time.Minute)) if err != nil { log.Fatal(err) } log.Printf("Aufgabe [%s] in die Warteschlange gestellt", info.ID)}
// Worker zur Verarbeitung von Aufgabenfunc handler(ctx context.Context, t *asynq.Task) error { log.Printf("Verarbeite Aufgabe: %s", t.Type()) // Hier die Logik zum E-Mail-Versand return nil}
func startWorker() { srv := asynq.NewServer( asynq.RedisClientOpt{Addr: "localhost:6379"}, asynq.Config{Concurrency: 10}, )
mux := asynq.NewServeMux() mux.HandleFunc(TypeEmailDelivery, handler)
if err := srv.Run(mux); err != nil { log.Fatal(err) }}Asynq ist ideal für die meisten Projekte, in denen bereits Redis verwendet wird. Es ist einfach einzurichten und bietet eine hervorragende Benutzererfahrung für Entwickler.
2. Machinery: Verteilte Aufgaben mit Brokern
Machinery ist eine ältere, aber sehr leistungsfähige Bibliothek, die von der Python-Bibliothek Celery inspiriert wurde. Im Gegensatz zu Asynq ist Machinery nicht fest an Redis gebunden und unterstützt mehrere Message Broker: Redis, RabbitMQ, AWS SQS und Memory.
Wesentliche Merkmale von Machinery:
- Broker-Flexibilität: Einfacher Wechsel zwischen verschiedenen Message Brokern.
- Aufgabenergebnisse: Integrierte Unterstützung zum Speichern und Abrufen von Aufgabenergebnissen (z. B. in Redis).
Auch in der Bibliothek
Prometheus-Metriken in Swift: Integration für iOS/macOS-Apps
OAuth-Client für C# (.NET): Implementierung der Autorisierung über Drittanbieterdienste
Aufgabenplaner in C# (.NET): Quartz.NET, Hangfire und Coravel
Swagger/OpenAPI-Generierung für Node.js (JavaScript): Tools und Beispiele