go-micro.dev/v5@v5.12.0/broker/memory.go (about)

     1  // Package memory provides a memory broker
     2  package broker
     3  
     4  import (
     5  	"errors"
     6  	"math/rand"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/google/uuid"
    11  	log "go-micro.dev/v5/logger"
    12  	maddr "go-micro.dev/v5/util/addr"
    13  	mnet "go-micro.dev/v5/util/net"
    14  )
    15  
    16  type memoryBroker struct {
    17  	opts *Options
    18  
    19  	Subscribers map[string][]*memorySubscriber
    20  
    21  	addr string
    22  	sync.RWMutex
    23  	connected bool
    24  }
    25  
    26  type memoryEvent struct {
    27  	err     error
    28  	message interface{}
    29  	opts    *Options
    30  	topic   string
    31  }
    32  
    33  type memorySubscriber struct {
    34  	opts    SubscribeOptions
    35  	exit    chan bool
    36  	handler Handler
    37  	id      string
    38  	topic   string
    39  }
    40  
    41  func (m *memoryBroker) Options() Options {
    42  	return *m.opts
    43  }
    44  
    45  func (m *memoryBroker) Address() string {
    46  	return m.addr
    47  }
    48  
    49  func (m *memoryBroker) Connect() error {
    50  	m.Lock()
    51  	defer m.Unlock()
    52  
    53  	if m.connected {
    54  		return nil
    55  	}
    56  
    57  	// use 127.0.0.1 to avoid scan of all network interfaces
    58  	addr, err := maddr.Extract("127.0.0.1")
    59  	if err != nil {
    60  		return err
    61  	}
    62  	i := rand.Intn(20000)
    63  	// set addr with port
    64  	addr = mnet.HostPort(addr, 10000+i)
    65  
    66  	m.addr = addr
    67  	m.connected = true
    68  
    69  	return nil
    70  }
    71  
    72  func (m *memoryBroker) Disconnect() error {
    73  	m.Lock()
    74  	defer m.Unlock()
    75  
    76  	if !m.connected {
    77  		return nil
    78  	}
    79  
    80  	m.connected = false
    81  
    82  	return nil
    83  }
    84  
    85  func (m *memoryBroker) Init(opts ...Option) error {
    86  	for _, o := range opts {
    87  		o(m.opts)
    88  	}
    89  	return nil
    90  }
    91  
    92  func (m *memoryBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
    93  	m.RLock()
    94  	if !m.connected {
    95  		m.RUnlock()
    96  		return errors.New("not connected")
    97  	}
    98  
    99  	subs, ok := m.Subscribers[topic]
   100  	m.RUnlock()
   101  	if !ok {
   102  		return nil
   103  	}
   104  
   105  	var v interface{}
   106  	if m.opts.Codec != nil {
   107  		buf, err := m.opts.Codec.Marshal(msg)
   108  		if err != nil {
   109  			return err
   110  		}
   111  		v = buf
   112  	} else {
   113  		v = msg
   114  	}
   115  
   116  	p := &memoryEvent{
   117  		topic:   topic,
   118  		message: v,
   119  		opts:    m.opts,
   120  	}
   121  
   122  	for _, sub := range subs {
   123  		if err := sub.handler(p); err != nil {
   124  			p.err = err
   125  			if eh := m.opts.ErrorHandler; eh != nil {
   126  				eh(p)
   127  				continue
   128  			}
   129  			return err
   130  		}
   131  	}
   132  
   133  	return nil
   134  }
   135  
   136  func (m *memoryBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
   137  	m.RLock()
   138  	if !m.connected {
   139  		m.RUnlock()
   140  		return nil, errors.New("not connected")
   141  	}
   142  	m.RUnlock()
   143  
   144  	var options SubscribeOptions
   145  	for _, o := range opts {
   146  		o(&options)
   147  	}
   148  
   149  	sub := &memorySubscriber{
   150  		exit:    make(chan bool, 1),
   151  		id:      uuid.New().String(),
   152  		topic:   topic,
   153  		handler: handler,
   154  		opts:    options,
   155  	}
   156  
   157  	m.Lock()
   158  	m.Subscribers[topic] = append(m.Subscribers[topic], sub)
   159  	m.Unlock()
   160  
   161  	go func() {
   162  		<-sub.exit
   163  		m.Lock()
   164  		var newSubscribers []*memorySubscriber
   165  		for _, sb := range m.Subscribers[topic] {
   166  			if sb.id == sub.id {
   167  				continue
   168  			}
   169  			newSubscribers = append(newSubscribers, sb)
   170  		}
   171  		m.Subscribers[topic] = newSubscribers
   172  		m.Unlock()
   173  	}()
   174  
   175  	return sub, nil
   176  }
   177  
   178  func (m *memoryBroker) String() string {
   179  	return "memory"
   180  }
   181  
   182  func (m *memoryEvent) Topic() string {
   183  	return m.topic
   184  }
   185  
   186  func (m *memoryEvent) Message() *Message {
   187  	switch v := m.message.(type) {
   188  	case *Message:
   189  		return v
   190  	case []byte:
   191  		msg := &Message{}
   192  		if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
   193  			m.opts.Logger.Logf(log.ErrorLevel, "[memory]: failed to unmarshal: %v\n", err)
   194  			return nil
   195  		}
   196  		return msg
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  func (m *memoryEvent) Ack() error {
   203  	return nil
   204  }
   205  
   206  func (m *memoryEvent) Error() error {
   207  	return m.err
   208  }
   209  
   210  func (m *memorySubscriber) Options() SubscribeOptions {
   211  	return m.opts
   212  }
   213  
   214  func (m *memorySubscriber) Topic() string {
   215  	return m.topic
   216  }
   217  
   218  func (m *memorySubscriber) Unsubscribe() error {
   219  	m.exit <- true
   220  	return nil
   221  }
   222  
   223  func NewMemoryBroker(opts ...Option) Broker {
   224  	options := NewOptions(opts...)
   225  
   226  	rand.Seed(time.Now().UnixNano())
   227  
   228  	return &memoryBroker{
   229  		opts:        options,
   230  		Subscribers: make(map[string][]*memorySubscriber),
   231  	}
   232  }