github.com/btccom/go-micro/v2@v2.9.3/broker/memory/memory.go (about)

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