gitee.com/lh-her-team/common@v1.5.1/msgbus/message_bus.go (about) 1 package msgbus 2 3 import ( 4 "sync" 5 ) 6 7 const ( 8 defaultMessageBufferSize = 10240 9 ) 10 11 // MessageBus provides a pub-sub interface for cross-module communication 12 type MessageBus interface { 13 // A subscriber register on s specific topic. 14 // When a message on this topic is published, the subscriber's OnMessage() is called. 15 Register(topic Topic, sub Subscriber) 16 // UnRegister unregister the subscriber from message bus. 17 UnRegister(topic Topic, sub Subscriber) 18 // Used to publish a message on this message bus to notify subscribers. 19 Publish(topic Topic, payload interface{}) 20 // Used to publish a message on this message bus to notify subscribers. 21 // Safe mode, make sure the subscriber's OnMessage() is called with the order of a message on this topic is published. 22 PublishSafe(topic Topic, payload interface{}) 23 // Used to publish a message on this message bus to notify subscribers. 24 // Sync mod, make sure all messages are completed sequentially. 25 PublishSync(topic Topic, payload interface{}) 26 // Close the message bus, all publishes are ignored. 27 Close() 28 } 29 30 // Subscriber should implement these methods, 31 type Subscriber interface { 32 // When a message with topic A is published on the message bus, 33 // all the subscribers's OnMessage() methods of topic A are called. 34 OnMessage(*Message) 35 // When the message bus is shutting down, 36 OnQuit() 37 } 38 39 type messageBusImpl struct { 40 mu sync.RWMutex 41 // topic -> subscriber list 42 // topicMap map[Topic]*list.List 43 topicMap sync.Map 44 once sync.Once 45 channelMap sync.Map 46 // messageC chan *Message 47 safeMessageC chan *Message 48 quitC chan struct{} 49 closed bool 50 } 51 52 func NewMessageBus() MessageBus { 53 return &messageBusImpl{ 54 mu: sync.RWMutex{}, 55 // topicMap: make(map[Topic]*list.List), 56 topicMap: sync.Map{}, 57 once: sync.Once{}, 58 channelMap: sync.Map{}, 59 // messageC: make(chan *Message, defaultMessageBufferSize), 60 safeMessageC: make(chan *Message, defaultMessageBufferSize), 61 quitC: make(chan struct{}), 62 closed: false, 63 } 64 } 65 66 type Message struct { 67 Topic Topic 68 Payload interface{} 69 } 70 71 // Register topic for subscriber 72 func (b *messageBusImpl) Register(topic Topic, sub Subscriber) { 73 b.once.Do(func() { 74 b.handleMessageLooping() 75 }) 76 // Lock to protect b.closed & slice of subscribers 77 b.mu.Lock() 78 defer b.mu.Unlock() 79 if b.closed { 80 return 81 } 82 r, _ := b.topicMap.Load(topic) 83 if r == nil { 84 // The topic has never been registered, make a new array to for subscribers 85 b.topicMap.Store(topic, make([]Subscriber, 0)) 86 // make one channel for each topic 87 msgC := make(chan *Message, defaultMessageBufferSize) 88 b.channelMap.Store(topic, msgC) 89 // start listening channel message for topic 90 go func() { 91 for { 92 select { 93 case <-b.quitC: 94 return 95 case m := <-msgC: 96 go b.notify(m, false) 97 } 98 } 99 }() 100 } 101 subs, _ := b.topicMap.Load(topic) 102 s, _ := subs.([]Subscriber) 103 // If the same subscriber instance has exist, then return to avoid redundancy 104 if isRedundant(s, sub) { 105 return 106 } 107 s = append(s, sub) 108 b.topicMap.Store(topic, s) 109 } 110 111 // UnRegister implements the MessageBus interface. 112 func (b *messageBusImpl) UnRegister(topic Topic, sub Subscriber) { 113 // Lock to protect b.closed & slice of subscribers 114 b.mu.Lock() 115 defer b.mu.Unlock() 116 if b.closed { 117 return 118 } 119 if subscribers, ok := b.topicMap.Load(topic); ok { 120 subs, _ := subscribers.([]Subscriber) 121 for i, s := range subs { 122 if s == sub { 123 newSubs := append(subs[:i], subs[i+1:]...) 124 b.topicMap.Store(topic, newSubs) 125 } 126 } 127 } 128 } 129 130 func (b *messageBusImpl) Publish(topic Topic, payload interface{}) { 131 b.mu.RLock() 132 defer b.mu.RUnlock() 133 if b.closed { 134 return 135 } 136 // fetch the channel for topic & push message into channel 137 c, _ := b.channelMap.Load(topic) 138 if c == nil { 139 //fmt.Printf("WARN: topic %s is not registered", topic.String()) // stop logging to console 140 return 141 } 142 channel, _ := c.(chan *Message) 143 channel <- &Message{Topic: topic, Payload: payload} 144 } 145 146 func (b *messageBusImpl) PublishSafe(topic Topic, payload interface{}) { 147 b.mu.RLock() 148 defer b.mu.RUnlock() 149 if b.closed { 150 return 151 } 152 b.safeMessageC <- &Message{Topic: topic, Payload: payload} 153 } 154 155 func (b *messageBusImpl) PublishSync(topic Topic, payload interface{}) { 156 val, _ := b.topicMap.Load(topic) 157 if val == nil { 158 return 159 } 160 subs, _ := val.([]Subscriber) 161 for _, sub := range subs { 162 sub.OnMessage(&Message{topic, payload}) 163 } 164 } 165 166 func (b *messageBusImpl) Close() { 167 // support multiple Close() 168 select { 169 case <-b.quitC: 170 default: 171 close(b.quitC) 172 b.channelMap.Range(func(_, v interface{}) bool { 173 channel, _ := v.(chan *Message) 174 close(channel) 175 return true 176 }) 177 // close(b.messageC) 178 close(b.safeMessageC) 179 } 180 } 181 182 func (b *messageBusImpl) handleMessageLooping() { 183 go func() { 184 for { 185 select { 186 case m := <-b.safeMessageC: 187 b.notify(m, true) 188 case <-b.quitC: 189 b.mu.Lock() 190 // for each top, notify subscribes that message bus is quiting now 191 b.topicMap.Range(func(_, v interface{}) bool { 192 s, _ := v.([]Subscriber) 193 length := len(s) 194 for i := 0; i < length; i++ { 195 s := s[i] 196 go s.OnQuit() 197 } 198 return true 199 }) 200 b.closed = true 201 b.mu.Unlock() 202 return 203 } 204 } 205 }() 206 } 207 208 // notify subscribers when msg comes 209 func (b *messageBusImpl) notify(m *Message, isSafe bool) { 210 if m.Topic <= 0 { 211 return 212 } 213 // fetch the subscribers for topic 214 val, _ := b.topicMap.Load(m.Topic) 215 if val == nil { 216 return 217 } 218 subs, _ := val.([]Subscriber) 219 for _, sub := range subs { 220 if isSafe { 221 sub.OnMessage(m) // notify in order 222 } else { 223 go sub.OnMessage(m) 224 } 225 } 226 } 227 228 func isRedundant(subs []Subscriber, sub Subscriber) bool { 229 for _, s := range subs { 230 if s == sub { 231 return true 232 } 233 } 234 return false 235 } 236 237 type DefaultSubscriber struct{} 238 239 func (DefaultSubscriber) OnMessage(*Message) { 240 // just for mock 241 } 242 243 func (DefaultSubscriber) OnQuit() { 244 // just for mock 245 }