> uploadtext_

v1.0.0 - Secure text sharing node

Basic In-Memory Publish-Subscribe System with Channels

Owner: SnippetBot Created: 2026-09-09 00:00:50 Size: 4.19 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 142
package main

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

// Broker manages subscriptions and publishes messages.
type Broker struct {
	mu          sync.RWMutex
	subscribers map[string][]chan string // Topic -> list of subscriber channels
}

func NewBroker() *Broker {
	return &Broker{
		subscribers: make(map[string][]chan string),
	}
}

// Subscribe registers a new subscriber for a given topic.
// Returns a channel on which messages for the topic will be received.
func (b *Broker) Subscribe(topic string) <-chan string {
	msgCh := make(chan string, 1)

	b.mu.Lock()
	defer b.mu.Unlock()

	b.subscribers[topic] = append(b.subscribers[topic], msgCh)
	fmt.Printf("Subscriber joined topic '%s'. Total subscribers: %d
", topic, len(b.subscribers[topic]))
	return msgCh
}

// Publish sends a message to all subscribers of a given topic.
func (b *Broker) Publish(topic, message string) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	subs := b.subscribers[topic]
	fmt.Printf("Publisher: Publishing '%s' to topic '%s' for %d subscribers.
", message, topic, len(subs))
	for _, subCh := range subs {
		// Use select with default to avoid blocking if a subscriber is slow
		select {
		case subCh <- message:
			// Message sent successfully
		default:
			fmt.Printf("Warning: Subscriber on topic '%s' is full, dropping message: %s
", topic, message)
		}
	}
}

// Unsubscribe removes a subscriber from a topic and closes its channel.
func (b *Broker) Unsubscribe(topic string, subCh <-chan string) {
	b.mu.Lock()
	defer b.mu.Unlock()

	subs := b.subscribers[topic]
	for i, ch := range subs {
		if ch == subCh {
			b.subscribers[topic] = append(subs[:i], subs[i+1:]...)
			close(ch) // Close the subscriber's channel
			fmt.Printf("Subscriber left topic '%s'. Remaining: %d
", topic, len(b.subscribers[topic]))
			return
		}
	}
}

func main() {
	broker := NewBroker()

	// Subscriber 1 for "news"
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		newsSubCh := broker.Subscribe("news")
		for msg := range newsSubCh {
			fmt.Printf("News Subscriber 1: %s
", msg)
			if msg == "The End" {
				fmt.Println("News Subscriber 1: Done receiving.")
				return
			}
		}
		fmt.Println("News Subscriber 1: Channel closed.")
	}()

	// Subscriber 2 for "news" and "alerts"
	wg.Add(1)
	go func() {
		defer wg.Done()
		newsSubCh := broker.Subscribe("news")
		alertSubCh := broker.Subscribe("alerts")
		for {
			select {
			case msg, ok := <-newsSubCh:
				if !ok { fmt.Println("News Subscriber 2 (news): Channel closed."); newsSubCh = nil }
				else { fmt.Printf("News Subscriber 2: %s
", msg) }
			case msg, ok := <-alertSubCh:
				if !ok { fmt.Println("News Subscriber 2 (alerts): Channel closed."); alertSubCh = nil }
				else { fmt.Printf("Alerts Subscriber 2: %s
", msg) }
			}
			if newsSubCh == nil && alertSubCh == nil {
				fmt.Println("News Subscriber 2: All channels closed, exiting.")
				return
			}
		}
	}()

	// Publisher sends messages
	time.Sleep(time.Millisecond * 100) // Give subscribers time to register
	broker.Publish("news", "Breaking News: Go channels are awesome!")
	broker.Publish("alerts", "Urgent: System maintenance tonight.")
	time.Sleep(time.Millisecond * 200)
	broker.Publish("news", "New feature release next week.")
	broker.Publish("alerts", "Reminder: Backups are running.")
	time.Sleep(time.Millisecond * 200)

	// Unsubscribe a subscriber (e.g., News Subscriber 1)
	// This requires keeping track of the channel returned by Subscribe for News Subscriber 1
	// For simplicity, let's assume we can't easily get it here directly without modifying the example.
	// In a real app, you'd store the returned 'newsSubCh' from `broker.Subscribe` in a variable.

	broker.Publish("news", "Final update: Server reboot complete.")
	broker.Publish("news", "The End") // Signal for a specific subscriber to stop

	// Give time for messages to be processed before main exits
	time.Sleep(time.Second * 1)
	// A more robust shutdown would involve signaling all subscribers to exit
	// and then closing the internal channels in the broker.
	wg.Wait() // Wait for subscribers to finish
	fmt.Println("Main: All messages published and subscribers processed.")

	// In a real system, you'd also need a way to close all subscriber channels 
	// managed by the broker when the broker itself shuts down.
}