概述
EventBus 像消息队列一样,可以用于异步处理消息数据,但实际上是单进程中的消息队列
go get github.com/tzq0301/eventbus
欢迎 Star 👏 GitHub | tzq0301/eventbus
快速上手
EventBus 库对外暴露两个接口,用于事件订阅与发布
PublishAsync(eventName string, data any) // 异步
Subscribe(eventName string, callback func(data any))
可以先定义事件名称与数据:
const (
EventCreate = "event.create"
EventUpdate = "event.update"
EventDelete = "event.delete"
)
type EventCreateData struct {
EventId int64
}
type EventUpdateData struct {
EventId int64
OperatorName string
}
type EventDeleteData struct {
EventId int64
OperatorName string
}
然后在程序中根据事件名称对事件进行订阅,并提供一个处理事件数据的函数:
func InitLark() {
SubScribe(EventCreate, func(data any) {
eventCreateData := data.(EventCreateData)
larkNotify(fmt.Sprintf("Create Event, Id = %v", eventCreateData.EventId))
})
SubScribe(EventUpdate, func(data any) {
eventUpdateData := data.(EventUpdateData)
larkNotify(fmt.Sprintf("Update Event, Id = %v, Updater = %v", eventUpdateData.EventId, eventUpdateData.OperatorName))
})
SubScribe(EventDelete, func(data any) {
eventDeleteData := data.(EventDeleteData)
larkNotify(fmt.Sprintf("Delete Event, Id = %v, Deleter = %v", eventDeleteData.EventId, eventDeleteData.OperatorName))
})
}
func InitResource() {
SubScribe(EventCreate, func(data any) {
eventCreateData := data.(EventCreateData)
createResource(eventCreateData.EventId)
})
SubScribe(EventDelete, func(data any) {
eventDeleteData := data.(EventDeleteData)
deleteResource(eventDeleteData.EventId)
})
}
在运行时,调用 PublishAsync 即可发布事件:
InitLark()
InitResource()
var wg sync.WaitGroup
wg.Add(4)
for i := 0; i < 4; i++ {
i64 := int64(i)
go func() {
PublishAsync(EventCreate, EventCreateData{
EventId: i64,
})
wg.Done()
}()
}
wg.Wait()
得到结果:
Create Resource, id = 3
Create Resource, id = 0
Create Resource, id = 1
Lark: Create Event, Id = 1
Create Resource, id = 2
Lark: Create Event, Id = 2
Lark: Create Event, Id = 3
Lark: Create Event, Id = 0
代码实现
EventBus 接口
// eventbus/eventbus.go
type EventBus interface {
PublishAsync(eventName string, data any)
Subscribe(eventName string, callback SubscribeCallback)
}
type eventBusImpl struct {
// eventName -> callback functions
eventNameToSubscribeCallbacks map[string][]SubscribeCallback
// 按照 EventBus 的逻辑,Subscribe 事件订阅一般都在服务端初始化时调用,
// 因此,在运行时,基本只有 PublishAsync 只读调用,因此在运行时基本只有读锁,
// 基本不会产生冲突,性能较高
mu sync.RWMutex
}
func (e *eventBusImpl) PublishAsync(eventName string, data any) {
// 异步处理事件发布,不会阻塞函数调用
go func() {
e.mu.RLock()
defer e.mu.RUnlock()
for _, callback := range e.eventNameToSubscribeCallbacks[eventName] {
go callback(data) // 所有的 Callback 回调函数并行调用(而非按序)
}
}()
}
func (e *eventBusImpl) Subscribe(eventName string, callback SubscribeCallback) {
e.mu.Lock()
defer e.mu.Unlock()
e.eventNameToSubscribeCallbacks[eventName] = append(e.eventNameToSubscribeCallbacks[eventName], callback)
}
提供一个默认的 EventBus 实例 & API 调用
为了易用性,创建一个默认的实例:
// eventbus/default.go
var DefaultEventBus = NewEventBus()
同样为了易用性,默认暴露该实例的 API 作为库函数的默认 API
// eventbus/api.go
var (
PublishAsync = DefaultEventBus.PublishAsync
SubScribe = DefaultEventBus.Subscribe
)