mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			192 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package eventbus
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"mayfly-go/pkg/logx"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
// BusSubscriber defines subscription-related bus behavior
 | 
						|
type BusSubscriber interface {
 | 
						|
	Subscribe(topic string, subId string, fn EventHandleFunc) error
 | 
						|
	SubscribeAsync(topic string, subId string, fn EventHandleFunc, transactional bool) error
 | 
						|
	SubscribeOnce(topic string, subId string, fn EventHandleFunc) error
 | 
						|
	Unsubscribe(topic string, subId string) error
 | 
						|
}
 | 
						|
 | 
						|
// BusPublisher defines publishing-related bus behavior
 | 
						|
type BusPublisher interface {
 | 
						|
	Publish(ctx context.Context, topic string, val any)
 | 
						|
}
 | 
						|
 | 
						|
// BusController defines bus control behavior (checking handler's presence, synchronization)
 | 
						|
type BusController interface {
 | 
						|
	WaitAsync()
 | 
						|
}
 | 
						|
 | 
						|
// Bus englobes global (subscribe, publish, control) bus behavior
 | 
						|
type Bus interface {
 | 
						|
	BusController
 | 
						|
	BusSubscriber
 | 
						|
	BusPublisher
 | 
						|
}
 | 
						|
 | 
						|
// EventBus - box for handlers and callbacks.
 | 
						|
type EventBus struct {
 | 
						|
	subscriberManager map[string]*SubscriberManager // topic -> SubscriberManager
 | 
						|
	lock              sync.Mutex                    // a lock for the map
 | 
						|
	wg                sync.WaitGroup
 | 
						|
}
 | 
						|
 | 
						|
func New() Bus {
 | 
						|
	b := &EventBus{
 | 
						|
		make(map[string]*SubscriberManager),
 | 
						|
		sync.Mutex{},
 | 
						|
		sync.WaitGroup{},
 | 
						|
	}
 | 
						|
	return Bus(b)
 | 
						|
}
 | 
						|
 | 
						|
type Event struct {
 | 
						|
	Topic string
 | 
						|
	Val   any
 | 
						|
}
 | 
						|
 | 
						|
// 订阅者们的事件处理器
 | 
						|
type SubscriberManager struct {
 | 
						|
	// 事件处理器 subId -> handler
 | 
						|
	handlers map[string]*eventHandler
 | 
						|
}
 | 
						|
 | 
						|
func NewSubscriberManager() *SubscriberManager {
 | 
						|
	return &SubscriberManager{
 | 
						|
		handlers: make(map[string]*eventHandler),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (sm *SubscriberManager) addSubscriber(subId string, eventHandler *eventHandler) {
 | 
						|
	sm.handlers[subId] = eventHandler
 | 
						|
}
 | 
						|
 | 
						|
func (sm *SubscriberManager) delSubscriber(subId string) {
 | 
						|
	delete(sm.handlers, subId)
 | 
						|
}
 | 
						|
 | 
						|
// 事件处理函数
 | 
						|
type EventHandleFunc func(ctx context.Context, event *Event) error
 | 
						|
 | 
						|
type eventHandler struct {
 | 
						|
	handlerFunc   EventHandleFunc // 事件处理函数
 | 
						|
	once          bool            // 是否只执行一次
 | 
						|
	async         bool
 | 
						|
	transactional bool
 | 
						|
 | 
						|
	sync.Mutex // lock for an event handler - useful for running async callbacks serially
 | 
						|
}
 | 
						|
 | 
						|
func (bus *EventBus) Subscribe(topic string, subId string, fn EventHandleFunc) error {
 | 
						|
	eh := &eventHandler{}
 | 
						|
	eh.handlerFunc = fn
 | 
						|
	return bus.doSubscribe(topic, subId, eh)
 | 
						|
}
 | 
						|
 | 
						|
// SubscribeAsync subscribes to a topic with an asynchronous callback
 | 
						|
// Transactional determines whether subsequent callbacks for a topic are
 | 
						|
func (bus *EventBus) SubscribeAsync(topic string, subId string, fn EventHandleFunc, transactional bool) error {
 | 
						|
	eh := &eventHandler{}
 | 
						|
	eh.handlerFunc = fn
 | 
						|
	eh.async = true
 | 
						|
	return bus.doSubscribe(topic, subId, eh)
 | 
						|
}
 | 
						|
 | 
						|
// SubscribeOnce subscribes to a topic once. Handler will be removed after executing.
 | 
						|
func (bus *EventBus) SubscribeOnce(topic string, subId string, fn EventHandleFunc) error {
 | 
						|
	eh := &eventHandler{}
 | 
						|
	eh.handlerFunc = fn
 | 
						|
	eh.once = true
 | 
						|
	return bus.doSubscribe(topic, subId, eh)
 | 
						|
}
 | 
						|
 | 
						|
func (bus *EventBus) Unsubscribe(topic string, subId string) error {
 | 
						|
	bus.lock.Lock()
 | 
						|
	defer bus.lock.Unlock()
 | 
						|
	subManager := bus.subscriberManager[topic]
 | 
						|
	if subManager == nil {
 | 
						|
		return errors.New("该主题不存在订阅者")
 | 
						|
	}
 | 
						|
	subManager.delSubscriber(subId)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (bus *EventBus) Publish(ctx context.Context, topic string, val any) {
 | 
						|
	bus.lock.Lock() // will unlock if handler is not found or always after setUpPublish
 | 
						|
	defer bus.lock.Unlock()
 | 
						|
	logx.Debugf("主题-[%s]-发布了事件", topic)
 | 
						|
	event := &Event{
 | 
						|
		Topic: topic,
 | 
						|
		Val:   val,
 | 
						|
	}
 | 
						|
	subscriberManager := bus.subscriberManager[topic]
 | 
						|
	if subscriberManager == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	handlers := subscriberManager.handlers
 | 
						|
	if len(handlers) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	for subId, handler := range handlers {
 | 
						|
		logx.Debugf("订阅者-[%s]-开始执行主题-[%s]-发布的事件", subId, topic)
 | 
						|
		if handler.once {
 | 
						|
			subscriberManager.delSubscriber(subId)
 | 
						|
		}
 | 
						|
		if !handler.async {
 | 
						|
			bus.doPublish(ctx, handler, event)
 | 
						|
		} else {
 | 
						|
			bus.wg.Add(1)
 | 
						|
			if handler.transactional {
 | 
						|
				bus.lock.Unlock()
 | 
						|
				handler.Lock()
 | 
						|
				bus.lock.Lock()
 | 
						|
			}
 | 
						|
			go bus.doPublishAsync(ctx, handler, event)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WaitAsync waits for all async callbacks to complete
 | 
						|
func (bus *EventBus) WaitAsync() {
 | 
						|
	bus.wg.Wait()
 | 
						|
}
 | 
						|
 | 
						|
func (bus *EventBus) doSubscribe(topic string, subId string, handler *eventHandler) error {
 | 
						|
	bus.lock.Lock()
 | 
						|
	defer bus.lock.Unlock()
 | 
						|
	logx.Debugf("订阅者-[%s]-订阅了主题-[%s]", subId, topic)
 | 
						|
	subManager := bus.subscriberManager[topic]
 | 
						|
	if subManager == nil {
 | 
						|
		subManager = NewSubscriberManager()
 | 
						|
		bus.subscriberManager[topic] = subManager
 | 
						|
	}
 | 
						|
	subManager.addSubscriber(subId, handler)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (bus *EventBus) doPublish(ctx context.Context, handler *eventHandler, event *Event) error {
 | 
						|
	err := handler.handlerFunc(ctx, event)
 | 
						|
	if err != nil {
 | 
						|
		logx.Errorf("订阅者执行主题[%s]失败: %s", event.Topic, err.Error())
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (bus *EventBus) doPublishAsync(ctx context.Context, handler *eventHandler, event *Event) {
 | 
						|
	defer bus.wg.Done()
 | 
						|
	if handler.transactional {
 | 
						|
		defer handler.Unlock()
 | 
						|
	}
 | 
						|
	bus.doPublish(ctx, handler, event)
 | 
						|
}
 |