Event Bus und das Observer-Muster in Go (Golang)
In der modernen Entwicklung mit Go (Golang) besteht häufig die Notwendigkeit, eine lose gekoppelte Interaktion zwischen den Komponenten einer Anwendung zu organisieren. Zwei klassische Ansätze sind das Observer-Muster und seine fortgeschrittenere Implementierung – der Event Bus (Ereignisbus). Diese Mechanismen ermöglichen es Teilen eines Programms, auf Aktionen anderer zu reagieren, ohne starre Abhängigkeiten zu schaffen.
In diesem Artikel werden wir erläutern, wie man einen Event Bus in reinem Go ohne externe Abhängigkeiten implementiert, das Observer-Muster betrachten und Codebeispiele für die synchrone und asynchrone Ereignisverarbeitung zeigen.
Was ist das Observer-Muster?
Das Observer-Muster (Beobachter) ist ein Verhaltensmuster der Softwareentwicklung, das einen Abonnementmechanismus schafft, der es Objekten ermöglicht, Ereignisse in anderen Objekten zu verfolgen und darauf zu reagieren.
Hauptelemente:
- Subject (Subjekt) – das Objekt, das Ereignisse erzeugt.
- Observer (Beobachter) – das Objekt, das Ereignisse abonniert.
- Event (Ereignis) – die Daten, die vom Subjekt an die Beobachter übermittelt werden.
Was ist ein Event Bus?
Ein Event Bus ist ein zentralisierter Bus, der die Übertragung von Ereignissen zwischen Herausgebern (publishers) und Abonnenten (subscribers) verwaltet. Im Gegensatz zum klassischen Observer, bei dem das Subjekt direkt eine Liste der Beobachter speichert, fungiert der Event Bus als Vermittler. Dies macht das System noch flexibler und skalierbarer.
Implementierung eines Event Bus in Go
Go hat keine eingebaute Implementierung eines Event Bus, aber er kann leicht mit Goroutinen, Kanälen und Interfaces geschrieben werden. Betrachten wir eine einfache, aber vollständige Implementierung.
1. Grundlegende Typen definieren
// Event repräsentiert eine Ereignisstrukturtype Event struct { Name string Data interface{}}
// EventHandler – eine Ereignisbehandlungsfunktiontype EventHandler func(Event)2. Event Bus erstellen
type EventBus struct { handlers map[string][]EventHandler mu sync.RWMutex}
// NewEventBus erstellt einen neuen Ereignisbusfunc NewEventBus() *EventBus { return &EventBus{ handlers: make(map[string][]EventHandler), }}
// Subscribe abonniert einen Handler für ein Ereignisfunc (eb *EventBus) Subscribe(name string, handler EventHandler) { eb.mu.Lock() defer eb.mu.Unlock() eb.handlers[name] = append(eb.handlers[name], handler)}
// Publish veröffentlicht ein Ereignis synchronfunc (eb *EventBus) Publish(name string, data interface{}) { eb.mu.RLock() defer eb.mu.RUnlock() if handlers, ok := eb.handlers[name]; ok { event := Event{Name: name, Data: data} for _, handler := range handlers { handler(event) } }}3. Asynchrone Veröffentlichung
Für die asynchrone Verarbeitung kann jeder Handler in einer separaten Goroutine gestartet werden:
// PublishAsync veröffentlicht ein Ereignis asynchronfunc (eb *EventBus) PublishAsync(name string, data interface{}) { eb.mu.RLock() defer eb.mu.RUnlock() if handlers, ok := eb.handlers[name]; ok { event := Event{Name: name, Data: data} for _, handler := range handlers { go handler(event) } }}Anwendungsbeispiel
Betrachten wir, wie wir unseren Event Bus in einer realen Anwendung verwenden. Angenommen, wir haben ein System, das Benutzeraktionen protokolliert und Benachrichtigungen sendet.
func main() { bus := NewEventBus()
// Abonnieren des Ereignisses "user.login" bus.Subscribe("user.login", func(e Event) { userData := e.Data.(map[string]string) fmt.Printf("Log: Benutzer %s hat sich angemeldet", userData["username"]) })
// Abonnieren desselben Ereignisses für Be Auch in der Bibliothek
Arbeiten mit Markdown in Go: Bibliotheken blackfriday, goldmark und chroma
PHP-Aufgabenplaner: cron-ähnliche Aufgaben ohne crond
Generierung von PDF-Berichten in PHP: Überblick über Bibliotheken und Beispiele