github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/broker/memory/memory.go (about)

     1  // Copyright 2020 Asim Aslam
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // Original source: github.com/micro/go-micro/v3/broker/memory/memory.go
    16  
    17  // Package memory provides a memory broker
    18  package memory
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"math/rand"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/google/uuid"
    28  	"github.com/tickoalcantara12/micro/v3/service/broker"
    29  	maddr "github.com/tickoalcantara12/micro/v3/util/addr"
    30  	mnet "github.com/tickoalcantara12/micro/v3/util/net"
    31  )
    32  
    33  type memoryBroker struct {
    34  	opts broker.Options
    35  
    36  	addr string
    37  	sync.RWMutex
    38  	connected   bool
    39  	Subscribers map[string][]*memorySubscriber
    40  }
    41  
    42  type memorySubscriber struct {
    43  	id      string
    44  	topic   string
    45  	exit    chan bool
    46  	handler broker.Handler
    47  	opts    broker.SubscribeOptions
    48  }
    49  
    50  func (m *memoryBroker) Options() broker.Options {
    51  	return m.opts
    52  }
    53  
    54  func (m *memoryBroker) Address() string {
    55  	return m.addr
    56  }
    57  
    58  func (m *memoryBroker) Connect() error {
    59  	m.Lock()
    60  	defer m.Unlock()
    61  
    62  	if m.connected {
    63  		return nil
    64  	}
    65  
    66  	// use 127.0.0.1 to avoid scan of all network interfaces
    67  	addr, err := maddr.Extract("127.0.0.1")
    68  	if err != nil {
    69  		return err
    70  	}
    71  	i := rand.Intn(20000)
    72  	// set addr with port
    73  	addr = mnet.HostPort(addr, 10000+i)
    74  
    75  	m.addr = addr
    76  	m.connected = true
    77  
    78  	return nil
    79  }
    80  
    81  func (m *memoryBroker) Disconnect() error {
    82  	m.Lock()
    83  	defer m.Unlock()
    84  
    85  	if !m.connected {
    86  		return nil
    87  	}
    88  
    89  	m.connected = false
    90  
    91  	return nil
    92  }
    93  
    94  func (m *memoryBroker) Init(opts ...broker.Option) error {
    95  	for _, o := range opts {
    96  		o(&m.opts)
    97  	}
    98  	return nil
    99  }
   100  
   101  func (m *memoryBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
   102  	m.RLock()
   103  	if !m.connected {
   104  		m.RUnlock()
   105  		return errors.New("not connected")
   106  	}
   107  
   108  	subs, ok := m.Subscribers[topic]
   109  	m.RUnlock()
   110  	if !ok {
   111  		return nil
   112  	}
   113  
   114  	for _, sub := range subs {
   115  		if err := sub.handler(msg); err != nil {
   116  			if eh := sub.opts.ErrorHandler; eh != nil {
   117  				eh(msg, err)
   118  			}
   119  			continue
   120  		}
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func (m *memoryBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
   127  	m.RLock()
   128  	if !m.connected {
   129  		m.RUnlock()
   130  		return nil, errors.New("not connected")
   131  	}
   132  	m.RUnlock()
   133  
   134  	var options broker.SubscribeOptions
   135  	for _, o := range opts {
   136  		o(&options)
   137  	}
   138  
   139  	sub := &memorySubscriber{
   140  		exit:    make(chan bool, 1),
   141  		id:      uuid.New().String(),
   142  		topic:   topic,
   143  		handler: handler,
   144  		opts:    options,
   145  	}
   146  
   147  	m.Lock()
   148  	m.Subscribers[topic] = append(m.Subscribers[topic], sub)
   149  	m.Unlock()
   150  
   151  	go func() {
   152  		<-sub.exit
   153  		m.Lock()
   154  		var newSubscribers []*memorySubscriber
   155  		for _, sb := range m.Subscribers[topic] {
   156  			if sb.id == sub.id {
   157  				continue
   158  			}
   159  			newSubscribers = append(newSubscribers, sb)
   160  		}
   161  		m.Subscribers[topic] = newSubscribers
   162  		m.Unlock()
   163  	}()
   164  
   165  	return sub, nil
   166  }
   167  
   168  func (m *memoryBroker) String() string {
   169  	return "memory"
   170  }
   171  
   172  func (m *memorySubscriber) Options() broker.SubscribeOptions {
   173  	return m.opts
   174  }
   175  
   176  func (m *memorySubscriber) Topic() string {
   177  	return m.topic
   178  }
   179  
   180  func (m *memorySubscriber) Unsubscribe() error {
   181  	m.exit <- true
   182  	return nil
   183  }
   184  
   185  func NewBroker(opts ...broker.Option) broker.Broker {
   186  	options := broker.Options{
   187  		Context: context.Background(),
   188  	}
   189  
   190  	rand.Seed(time.Now().UnixNano())
   191  	for _, o := range opts {
   192  		o(&options)
   193  	}
   194  
   195  	return &memoryBroker{
   196  		opts:        options,
   197  		Subscribers: make(map[string][]*memorySubscriber),
   198  	}
   199  }