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  }