Simple Event Bus / Pub-Sub Implementation with Go Channels
Owner: SnippetBot
Created: 2026-07-18 00:00:40
Size: 3.58 KB
Expires: Never
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"fmt"
"sync"
"time"
)
// Event represents a generic event
type Event interface{}
// EventBus manages event subscriptions and publishing
type EventBus struct {
subscribers map[string][]chan Event
mu sync.RWMutex
}
// NewEventBus creates a new EventBus instance
func NewEventBus() *EventBus {
return &EventBus{
subscribers: make(map[string][]chan Event),
}
}
// Publish sends an event to all subscribers of a specific topic
func (eb *EventBus) Publish(topic string, event Event) {
eb.mu.RLock()
defer eb.mu.RUnlock()
if channels, found := eb.subscribers[topic]; found {
for _, ch := range channels {
// Using non-blocking send to avoid blocking the publisher if a subscriber is slow
select {
case ch <- event:
default:
fmt.Printf("Warning: Subscriber for topic '%s' not ready, dropping event: %v
", topic, event)
}
}
}
}
// Subscribe adds a channel to listen for events on a specific topic
// Returns the subscriber channel and a function to unsubscribe.
func (eb *EventBus) Subscribe(topic string) (<-chan Event, func()) {
ch := make(chan Event, 10) // Buffered channel for subscriber
eb.mu.Lock()
eb.subscribers[topic] = append(eb.subscribers[topic], ch)
eb.mu.Unlock()
unsubscribe := func() {
eb.mu.Lock()
defer eb.mu.Unlock()
if channels, found := eb.subscribers[topic]; found {
for i, subscriberCh := range channels {
if subscriberCh == ch {
// Remove the channel
eb.subscribers[topic] = append(channels[:i], channels[i+1:]...)
close(ch) // Close the subscriber's channel
return
}
}
}
}
return ch, unsubscribe
}
func main() {
bus := NewEventBus()
var wg sync.WaitGroup
// Subscriber 1 for "user_events"
userSub1, unsubscribeUser1 := bus.Subscribe("user_events")
wg.Add(1)
go func() {
defer wg.Done()
for event := range userSub1 {
fmt.Printf("User Subscriber 1 received: %v
", event)
}
fmt.Println("User Subscriber 1 stopped.")
}()
// Subscriber 2 for "order_events"
orderSub1, unsubscribeOrder1 := bus.Subscribe("order_events")
wg.Add(1)
go func() {
defer wg.Done()
for event := range orderSub1 {
fmt.Printf("Order Subscriber 1 received: %v
", event)
}
fmt.Println("Order Subscriber 1 stopped.")
}()
// Subscriber 3 for both "user_events" and "order_events"
userSub2, unsubscribeUser2 := bus.Subscribe("user_events")
wg.Add(1)
go func() {
defer wg.Done()
for event := range userSub2 {
fmt.Printf("User Subscriber 2 received: %v
", event)
}
fmt.Println("User Subscriber 2 stopped.
") }()
orderSub2, unsubscribeOrder2 := bus.Subscribe("order_events")
wg.Add(1)
go func() {
defer wg.Done()
for event := range orderSub2 {
fmt.Printf("Order Subscriber 2 received: %v
", event)
}
fmt.Println("Order Subscriber 2 stopped.")
}()
time.Sleep(10 * time.Millisecond) // Give subscribers time to start
// Publish events
bus.Publish("user_events", "UserRegistered: Alice")
bus.Publish("order_events", "OrderCreated: #12345")
bus.Publish("user_events", "UserLoggedIn: Bob")
bus.Publish("system_events", "SystemRestarted") // No subscribers for this topic
time.Sleep(50 * time.Millisecond) // Give time for events to be processed
// Unsubscribe one listener
unsubscribeUser1()
fmt.Println("Unsubscribed User Subscriber 1.")
bus.Publish("user_events", "UserLoggedOut: Alice") // Only UserSub2 should get this
bus.Publish("order_events", "OrderShipped: #12345")
time.Sleep(50 * time.Millisecond)
// Unsubscribe all
unsubscribeUser2()
unsubscribeOrder1()
unsubscribeOrder2()
fmt.Println("All subscribers unsubscribed.")
wg.Wait()
fmt.Println("Main finished.")
}