> uploadtext_

v1.0.0 - Secure text sharing node

Implementing a Simple Publish-Subscribe Event Bus

Owner: SnippetBot Created: 2026-08-21 00:00:33 Size: 3.56 KB Expires: Never
[ RAW ] [ NEW ]
tty1
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
package main

import (
	"fmt"
	"sync"
	"time"
)

// Event represents a message to be published.
type Event struct {
	Topic string
	Data  string
}

// Subscriber is a channel that receives events.
type Subscriber chan Event

// EventBus manages subscriptions and event publishing.
type EventBus struct {
	subscribers map[string][]Subscriber
	mu          sync.RWMutex
}

// NewEventBus creates and returns a new EventBus.
func NewEventBus() *EventBus {
	return &EventBus{
		subscribers: make(map[string][]Subscriber),
	}
}

// Subscribe adds a new subscriber for a given topic.
// It returns a channel where the subscriber will receive events.
func (eb *EventBus) Subscribe(topic string) Subscriber {
	eb.mu.Lock()
	defer eb.mu.Unlock()

	ch := make(Subscriber, 10) // Buffered channel to avoid blocking publisher immediately
	eb.subscribers[topic] = append(eb.subscribers[topic], ch)
	fmt.Printf("Subscribed to topic '%s'.
", topic)
	return ch
}

// Unsubscribe removes a subscriber from a topic.
func (eb *EventBus) Unsubscribe(topic string, sub Subscriber) {
	eb.mu.Lock()
	defer eb.mu.Unlock()

	if subs, ok := eb.subscribers[topic]; ok {
		for i, s := range subs {
			if s == sub {
				eb.subscribers[topic] = append(subs[:i], subs[i+1:]...)
				close(s) // Close the subscriber's channel
				fmt.Printf("Unsubscribed from topic '%s'.
", topic)
				return
			}
		}
	}
}

// Publish sends an event to all subscribers of the event's topic.
func (eb *EventBus) Publish(event Event) {
	eb.mu.RLock()
	defer eb.mu.RUnlock()

	if subs, ok := eb.subscribers[event.Topic]; ok {
		fmt.Printf("Publishing event to topic '%s': %s
", event.Topic, event.Data)
		for _, sub := range subs {
			// Use a select with a default to avoid blocking the publisher
			// if a subscriber is slow to consume messages.
			select {
			case sub <- event:
				// Sent successfully
			default:
				fmt.Printf("Warning: Subscriber for topic '%s' is full, dropping event: %s
", event.Topic, event.Data)
			}
		}
	}
}

func main() {
	bus := NewEventBus()
	var wg sync.WaitGroup

	// Consumer 1 for "user_events"
	wg.Add(1)
	go func() {
		defer wg.Done()
		sub1 := bus.Subscribe("user_events")
		for event := range sub1 {
			fmt.Printf("[Consumer 1] Received event on topic '%s': %s
", event.Topic, event.Data)
		}
		fmt.Println("[Consumer 1] Stopped.")
	}()

	// Consumer 2 for "user_events" and "system_events"
	wg.Add(1)
	go func() {
		defer wg.Done()
		sub2 := bus.Subscribe("user_events")
		sub3 := bus.Subscribe("system_events")
		for {
			select {
			case event, ok := <-sub2:
				if !ok { return } // Channel closed
				fmt.Printf("[Consumer 2] User event: %s
", event.Data)
			case event, ok := <-sub3:
				if !ok { return } // Channel closed
				fmt.Printf("[Consumer 2] System event: %s
", event.Data)
			case <-time.After(5 * time.Second):
				fmt.Println("[Consumer 2] No events for 5s, unsubscribing from system_events.")
				bus.Unsubscribe("system_events", sub3)
				return // Exit this goroutine
			}
		}
	}()

	// Give consumers time to subscribe
	time.Sleep(100 * time.Millisecond)

	// Publishers
	bus.Publish(Event{Topic: "user_events", Data: "User registered: Alice"})
	time.Sleep(200 * time.Millisecond)
	bus.Publish(Event{Topic: "system_events", Data: "Database backup started"})
	time.Sleep(200 * time.Millisecond)
	bus.Publish(Event{Topic: "user_events", Data: "User logged in: Bob"})
	time.Sleep(200 * time.Millisecond)
	bus.Publish(Event{Topic: "system_events", Data: "CPU usage high"})
	time.Sleep(200 * time.Millisecond)

	// Wait for all goroutines to finish (e.g., Consumer 2 will timeout and exit)
	wg.Wait()
	fmt.Println("Main finished.")
}