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 }