1. 实现了消息信道
2. 信道可关闭, 发送消息前, 可通过获取信道状态决定发送到哪条信道
通过这种方法, 可以实现消息在上云和存本地之间灵活切换, 如果网络或者其它原因导致不能上云, 可切换至本地存储, 待上云通道恢复后, 则可有本地重新发布至上云信道
信道代码:
package EventBus
import "sync"
type DataEvent struct {
Data interface{}
Topic string
}
type DataChannel chan DataEvent
type DataChannelSlice []DataChannel
type EventBus struct {
Subscribers map[string]DataChannelSlice
rm sync.RWMutex
Status map[string]bool
}
func (eb *EventBus) Publish(topic string, data interface{}) {
eb.rm.RLock()
if chans, found := eb.Subscribers[topic]; found {
channels := append(DataChannelSlice{}, chans...)
go func(data DataEvent, dataChannelSlices DataChannelSlice) {
for _, ch := range dataChannelSlices {
ch <- data
}
}(DataEvent{data, topic}, channels)
}
eb.rm.RUnlock()
}
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.Status[topic] = true
eb.rm.Unlock()
}
func (eb *EventBus) HandleChannel(topic string, status bool) {
eb.rm.Lock()
if _, ok := eb.Status[topic]; !ok {
return
}
eb.Status[topic] = status
eb.rm.Unlock()
}
func (eb *EventBus) CheckStatus(topic string) bool {
eb.rm.RLock()
if _, ok := eb.Status[topic]; !ok {
return false
}
status := eb.Status[topic]
eb.rm.RUnlock()
return status
}
调用的demo代码
package main
import (
"Playground/EventBus"
"fmt"
"time"
)
var eb = &EventBus.EventBus{
Subscribers: map[string]EventBus.DataChannelSlice{},
Status: map[string]bool{},
}
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) {
status := eb.CheckStatus(topic)
if status {
eb.Publish(topic, data)
} else {
fmt.Printf("%s is closed\n", topic)
}
}
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 func() {
for {
if eb.CheckStatus("topic1") {
publishTo("topic1", "Welcome to topic-1")
} else if eb.CheckStatus("topic2") {
publishTo("topic2", "Welcome to topic-2")
} else {
fmt.Println("Can not found valid channel")
}
time.Sleep(time.Millisecond * 10)
}
}()
go func() {
for {
select {
case d := <-ch1:
go PrintDataEvent("ch1", d)
case d := <-ch2:
go PrintDataEvent("ch2", d)
case d := <-ch3:
go PrintDataEvent("ch3", d)
}
}
}()
i := 0
for {
if i%10 == 2 {
eb.HandleChannel("topic1", false)
} else if i%10 == 3 {
eb.HandleChannel("topic2", false)
} else if i%10 == 5 {
eb.HandleChannel("topic1", true)
} else if i%10 == 7 {
eb.HandleChannel("topic2", true)
}
i += 1
}
}