Golang學習篇—實現簡單的事件總線(發佈訂閱模式)

1.事件總線

事件總線是發佈/訂閱模式的實現,其中發佈者發佈數據,並且訂閱者可以監聽這些數據並基於這些數據作出處理。這使發佈者與訂閱者鬆耦合。發佈者將數據事件發佈到事件總線,總線負責將它們發送給訂閱者。

傳統的實現事件總線的方法會涉及到使用回調。訂閱者通常實現接口,然後事件總線通過接口傳播數據。

使用 Go 的併發模型,大多數地方可以使用 channel 來替代回調。

2.事件總線實例 

EvevtBus.go   

package EventBus

import "sync"

// 定義數據結構
type DataEvent struct {
	Data  interface{}
	Topic string
}

// DataChannel是一個能接收 DataEvent 的 channel
type DataChannel chan DataEvent

// DataChannelSlice 是一個包含 DataChannels 數據的切片
type DataChannelSlice []DataChannel



// 定義事件總線  EventBus 存儲有關訂閱者感興趣的特定主題的信息
type EventBus struct {
	Subscribers map[string]DataChannelSlice
	rm          sync.RWMutex
}

// 發佈主題 發佈者需要提供廣播給訂閱者所需要的主題和數據
func (eb *EventBus) Publish(topic string, data interface{}) {
	eb.rm.RLock()
	if chans, found := eb.Subscribers[topic]; found {
		// 這樣做是因爲切片引用相同的數組,即使它們是按值傳遞的
		// 因此我們正在使用我們的元素創建一個新切片,從而正確地保持鎖定
		channels := append(DataChannelSlice{}, chans...) //切片賦值

		//使用Goroutine 來避免阻塞發佈者
		go func(data DataEvent, dataChannelSlices DataChannelSlice) {
			for _, ch := range dataChannelSlices {
				ch <- data
			}
		}(DataEvent{Data: data, Topic: topic}, channels)
	}
	eb.rm.RUnlock()
}

// 訂閱主題  如傳統方法回調一樣。當發佈者向主題發佈數據時,channel將接收數據。
func (eb *EventBus) Subscribe(topic string, ch DataChannel) {
	eb.rm.Lock()
	if prev, found := eb.Subscribers[topic]; found {
		eb.Subscribers[topic] = append(prev, ch)
	} else {
		eb.Subscribers[topic] = append([]DataChannel{}, ch)
	}
	eb.rm.Unlock()
}

 main.go調用

package main

import (
	"Alang/EventBus/EventBus"
	"fmt"
	"math/rand"
	"time"
)

// 聲明事件總線對象
var eb = &EventBus.EventBus{
	Subscribers: map[string]EventBus.DataChannelSlice{},
}

//打印訂閱消息
func printDataEvent(ch string, data EventBus.DataEvent) {
	fmt.Printf("Channel: %s; Topic: %s; DataEvent: %v\n", ch, data.Topic, data.Data)
}

//發佈消息
func publishTo(topic string, data string) {
	for {
		eb.Publish(topic, data)
		time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
	}
}

func main() {

	ch1 := make(chan EventBus.DataEvent)
	ch2 := make(chan EventBus.DataEvent)
	ch3 := make(chan EventBus.DataEvent)

	eb.Subscribe("topic1", ch1)
	eb.Subscribe("topic2", ch2)
	eb.Subscribe("topic3", ch3)

	go publishTo("topic1", "Welcome to topic-1")
	go publishTo("topic2", "Welcome to topic-2")

	for {
		select {
		case d := <-ch1:
			go printDataEvent("ch1", d)
		case d := <-ch2:
			go printDataEvent("ch2", d)
		case d := <-ch3:
			go printDataEvent("ch3", d)
		}
	}
}

運行結果: 

侷限性:channel 如果沒有訂閱者消費,會阻塞


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章