gitee.com/hongliu9527/go-tools@v0.0.8/eventbus/eventbus.go (about)

     1  /*
     2   * @Author: hongliu
     3   * @Date: 2022-12-16 10:26:12
     4   * @LastEditors: hongliu
     5   * @LastEditTime: 2022-12-16 11:33:57
     6   * @FilePath: \go-tools\eventbus\eventbus.go
     7   * @Description: 事件总线
     8   *
     9   * Copyright (c) 2022 by 洪流, All Rights Reserved.
    10   */
    11  
    12  package eventbus
    13  
    14  import (
    15  	"fmt"
    16  	"reflect"
    17  	"sync"
    18  	"time"
    19  )
    20  
    21  // BusSubscriber defines subscription-related bus behavior
    22  type BusSubscriber interface {
    23  	Subscribe(topic string, fn interface{}) error
    24  	Unsubscribe(topic string, handler interface{}) error
    25  }
    26  
    27  // BusPublisher defines publishing-related bus behavior
    28  type BusPublisher interface {
    29  	Publish(topic string, args ...interface{})
    30  	PublishWithReply(topic string, timeout time.Duration, args ...interface{}) (interface{}, error) // 目前只支持返回一个函数的handler
    31  }
    32  
    33  // BusController defines bus control behavior (checking handler's presence, synchronization)
    34  type BusController interface {
    35  	HasCallback(topic string) bool
    36  }
    37  
    38  // Bus englobes global (subscribe, publish, control) bus behavior
    39  type Bus interface {
    40  	BusController
    41  	BusSubscriber
    42  	BusPublisher
    43  }
    44  
    45  // EventBus - box for handlers and callbacks.
    46  type EventBus struct {
    47  	handlers map[string][]*eventHandler
    48  	lock     sync.RWMutex // a rwlock for the map
    49  }
    50  
    51  type eventHandler struct {
    52  	callBack   reflect.Value
    53  	sync.Mutex // lock for an event handler - useful for running async callbacks serially
    54  }
    55  
    56  // New returns new EventBus with empty handlers.
    57  func New() Bus {
    58  	b := &EventBus{
    59  		make(map[string][]*eventHandler),
    60  		sync.RWMutex{},
    61  	}
    62  	return Bus(b)
    63  }
    64  
    65  // doSubscribe handles the subscription logic and is utilized by the public Subscribe functions
    66  func (bus *EventBus) doSubscribe(topic string, fn interface{}, handler *eventHandler) error {
    67  	bus.lock.Lock()
    68  	defer bus.lock.Unlock()
    69  	if !(reflect.TypeOf(fn).Kind() == reflect.Func) {
    70  		return fmt.Errorf("%s is not of type reflect.Func", reflect.TypeOf(fn).Kind())
    71  	}
    72  
    73  	bus.handlers[topic] = append(bus.handlers[topic], handler)
    74  	return nil
    75  }
    76  
    77  // Subscribe subscribes to a topic.
    78  // Returns error if `fn` is not a function.
    79  func (bus *EventBus) Subscribe(topic string, fn interface{}) error {
    80  
    81  	return bus.doSubscribe(topic, fn, &eventHandler{
    82  		reflect.ValueOf(fn), sync.Mutex{},
    83  	})
    84  }
    85  
    86  // HasCallback returns true if exists any callback subscribed to the topic.
    87  func (bus *EventBus) HasCallback(topic string) bool {
    88  	bus.lock.Lock()
    89  	defer bus.lock.Unlock()
    90  	_, ok := bus.handlers[topic]
    91  	if ok {
    92  		return len(bus.handlers[topic]) > 0
    93  	}
    94  	return false
    95  }
    96  
    97  // Unsubscribe removes callback defined for a topic.
    98  // Returns error if there are no callbacks subscribed to the topic.
    99  func (bus *EventBus) Unsubscribe(topic string, handler interface{}) error {
   100  	bus.lock.Lock()
   101  	defer bus.lock.Unlock()
   102  	if _, ok := bus.handlers[topic]; ok && len(bus.handlers[topic]) > 0 {
   103  		bus.removeHandler(topic, bus.findHandlerIdx(topic, reflect.ValueOf(handler)))
   104  		return nil
   105  	}
   106  	return fmt.Errorf("topic %s doesn't exist", topic)
   107  }
   108  
   109  // Publish executes callback defined for a topic. Any additional argument will be transferred to the callback.
   110  func (bus *EventBus) Publish(topic string, args ...interface{}) {
   111  	bus.lock.RLock() // will unlock if handler is not found or always after setUpPublish
   112  	defer bus.lock.RUnlock()
   113  	if handlers, ok := bus.handlers[topic]; ok && 0 < len(handlers) {
   114  		// Handlers slice may be changed by removeHandler and Unsubscribe during iteration,
   115  		// so make a copy and iterate the copied slice.
   116  		copyHandlers := make([]*eventHandler, len(handlers))
   117  		copy(copyHandlers, handlers)
   118  		for _, handler := range copyHandlers {
   119  			go bus.doPublish(handler, topic, args...)
   120  		}
   121  	}
   122  }
   123  
   124  // 向一个主题推送消息,支持单handler的主题,并返回这个handler的值
   125  func (bus *EventBus) PublishWithReply(topic string, timeout time.Duration, args ...interface{}) (interface{}, error) {
   126  	bus.lock.RLock()
   127  	defer bus.lock.RUnlock()
   128  
   129  	// 如果对应的topic有多个则返回error
   130  	handlers, ok := bus.handlers[topic]
   131  	if !ok {
   132  		return nil, fmt.Errorf("topic(%s)没有注册", topic)
   133  	}
   134  	handlerNum := len(handlers)
   135  	if handlerNum != 1 {
   136  		return nil, fmt.Errorf("topic(%s)对应的handler个数错误[期望值: 1,实际值: %d]", topic, handlerNum)
   137  	}
   138  	copyHandlers := make([]*eventHandler, len(handlers))
   139  	copy(copyHandlers, handlers)
   140  
   141  	// 超时控制通道
   142  	done := make(chan []reflect.Value, 1)
   143  	defer close(done)
   144  	go func() {
   145  		defer func() {
   146  			if err := recover(); err != nil {
   147  				fmt.Printf("PublishWithReply painc (%s)\n", err)
   148  			}
   149  		}()
   150  		passedArguments := bus.setUpPublish(copyHandlers[0], args...)
   151  		resultList := copyHandlers[0].callBack.Call(passedArguments)
   152  		done <- resultList
   153  	}()
   154  
   155  	select {
   156  	case resultList := <-done:
   157  		if len(resultList) == 1 {
   158  			return resultList[0], nil
   159  		}
   160  		return nil, nil
   161  	case <-time.After(timeout): // 超时返回
   162  		return nil, fmt.Errorf("topic(%s)对应的handler执行超时", topic)
   163  	}
   164  }
   165  
   166  func (bus *EventBus) doPublish(handler *eventHandler, topic string, args ...interface{}) {
   167  	passedArguments := bus.setUpPublish(handler, args...)
   168  	handler.callBack.Call(passedArguments)
   169  }
   170  
   171  func (bus *EventBus) removeHandler(topic string, idx int) {
   172  	if _, ok := bus.handlers[topic]; !ok {
   173  		return
   174  	}
   175  	l := len(bus.handlers[topic])
   176  
   177  	if !(0 <= idx && idx < l) {
   178  		return
   179  	}
   180  
   181  	copy(bus.handlers[topic][idx:], bus.handlers[topic][idx+1:])
   182  	bus.handlers[topic][l-1] = nil // or the zero value of T
   183  	bus.handlers[topic] = bus.handlers[topic][:l-1]
   184  }
   185  
   186  func (bus *EventBus) findHandlerIdx(topic string, callback reflect.Value) int {
   187  	if _, ok := bus.handlers[topic]; ok {
   188  		for idx, handler := range bus.handlers[topic] {
   189  			if handler.callBack.Type() == callback.Type() &&
   190  				handler.callBack.Pointer() == callback.Pointer() {
   191  				return idx
   192  			}
   193  		}
   194  	}
   195  	return -1
   196  }
   197  
   198  func (bus *EventBus) setUpPublish(callback *eventHandler, args ...interface{}) []reflect.Value {
   199  	funcType := callback.callBack.Type()
   200  	passedArguments := make([]reflect.Value, len(args))
   201  	for i, v := range args {
   202  		if v == nil {
   203  			passedArguments[i] = reflect.New(funcType.In(i)).Elem()
   204  		} else {
   205  			passedArguments[i] = reflect.ValueOf(v)
   206  		}
   207  	}
   208  
   209  	return passedArguments
   210  }