Badu/bus:Go 轻量级泛型发布/订阅事件总线

17 阅读3分钟

badu/bus (github.com/badu/bus) 是一个 Go 语言基于泛型实现的轻量级发布-订阅(Pub/Sub)事件总线库。它允许不同的组件通过事件机制进行解耦通信,而不需要直接调用彼此的函数。

它解决了什么问题?

  • 组件解耦:发布者和订阅者彼此无感知,更灵活、可扩展,便于测试。
  • 异步消息:消息可以异步发送和接收,提高系统性能和响应速度。
  • 模块化:有利于将系统拆分为更小、可维护的模块。

安装

go get github.com/badu/bus

示例

我们构建一个解耦的订单系统,它的业务逻辑包括:

  • 订单创建(order 模块)
  • 通知用户(notification 模块)
  • 记录日志(logging 模块)
  • 自动发货(shipping 模块)

当用户下单时:订单系统只负责发布事件,不会直接调用通知、日志、发货等模块。其他模块独立监听事件,执行自己的逻辑。

项目结构

📦 order-system
├── 📂 events           # 事件模块
│   ├── events.go       # 事件
├── 📂 order            # 订单模块
│   ├── order.go        # 订单创建
├── 📂 notification     # 通知模块
│   ├── notify.go       # 发送通知
├── 📂 logging          # 日志模块
│   ├── log.go          # 记录日志
├── 📂 shipping         # 物流模块
│   ├── ship.go         # 自动发货
├── main.go             # 启动程序

事件(events/events.go)

首先,始终将事件放在单独的 events 包中,避免循环依赖。所有模块都会使用它:

package events

// OrderEvent 订单事件
type OrderEvent struct {
 ID string
}

订单模块(order/order.go)

订单模块负责创建订单并发布事件,但不关心其他模块如何处理订单。

package order

import (
 "fmt"
 "github.com/badu/bus"
 "order-system/events"
)

// PlaceOrder 下单函数
func PlaceOrder(orderID string) {
 fmt.Println("🛒 订单已创建:", orderID)

 // 订单创建后,发布 "OrderEvent" 事件
 bus.Pub(events.OrderEvent{ID: orderID})
}

通知模块(notification/notify.go)

监听订单事件,发送通知。

package notification

import (
 "fmt"
 "github.com/badu/bus"
 "order-system/events"
)

// NotifyUser 发送通知
func NotifyUser(event events.OrderEvent) {
 fmt.Println("📢 通知用户: 订单", event.ID, "已创建")
}

// Init 初始化时订阅事件
func Init() {
 bus.Sub(NotifyUser)
}

日志模块(logging/log.go)

监听订单事件,记录日志。

package logging

import (
 "fmt"
 "github.com/badu/bus"
 "order-system/events"
)

// LogOrder 记录订单日志
func LogOrder(event events.OrderEvent) {
 fmt.Println("📝 记录日志: 订单", event.ID, "已创建")
}

// Init 初始化时订阅事件
func Init() {
 bus.Sub(LogOrder)
}

物流模块(shipping/ship.go)

监听订单事件,自动发货。

package shipping

import (
 "fmt"
 "github.com/badu/bus"
 "order-system/events"
)

// AutoShip 处理自动发货
func AutoShip(event events.OrderEvent) {
 fmt.Println("🚚 订单", event.ID, "已进入发货流程")
}

// Init 初始化时订阅事件
func Init() {
 bus.Sub(AutoShip)
}

主函数(main.go)

在 main.go 中,我们初始化所有订阅者,触发订单创建。

package main

import (
 "order-system/logging"
 "order-system/notification"
 "order-system/order"
 "order-system/shipping"
)

func main() {
 // 初始化事件订阅
 notification.Init()
 logging.Init()
 shipping.Init()

 // 用户下单
 order.PlaceOrder("12345")
}

运行结果

🛒 订单已创建: 12345
📢 通知用户: 订单 12345 已创建
📝 记录日志: 订单 12345 已创建
🚚 订单 12345 已进入发货流程

order 模块只负责订单逻辑,而 notification、logging、shipping 模块各自监听事件,独立执行逻辑。

常见问题

示例

// InterestingEvent 定义事件
type InterestingEvent struct {
}

// OnMyEventOccurred 处理函数
func OnMyEventOccurred(event InterestingEvent) {
 // do something with the event here
}


bus.Sub(OnMyEventOccurred) // 订阅

bus.Pub(InterestingEvent{}) // 发布

如何取消订阅:

 subscription := bus.Sub(OnMyEventOccurred)
 // 需要时调用 cancel,OnMyEventOccurred 函数将不再被调用
 subscription.Cancel()

只订阅一次:

// OnMyEventOccurred 处理函数
func OnMyEventOccurred(event InterestingEventbool {
 // do something with the event here
 return true // 事件处理器需返回 true
}

bus.SubCancel(OnMyEventOccurred) // 订阅

异步处理事件:

 bus.PubAsync(InterestingEvent{}) // 发布

查看已注册的事件:

 bus.Range(func(k, v any) bool {
  fmt.Printf("%#v %#v\n", k, v)
  return true
 })

References
github.com/badu/bus