github.com/micro/go-micro/v2@v2.9.1/registry/memory/memory.go (about)

     1  // Package memory provides an in-memory registry
     2  package memory
     3  
     4  import (
     5  	"context"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/google/uuid"
    10  	"github.com/micro/go-micro/v2/logger"
    11  	"github.com/micro/go-micro/v2/registry"
    12  )
    13  
    14  var (
    15  	sendEventTime = 10 * time.Millisecond
    16  	ttlPruneTime  = time.Second
    17  )
    18  
    19  type node struct {
    20  	*registry.Node
    21  	TTL      time.Duration
    22  	LastSeen time.Time
    23  }
    24  
    25  type record struct {
    26  	Name      string
    27  	Version   string
    28  	Metadata  map[string]string
    29  	Nodes     map[string]*node
    30  	Endpoints []*registry.Endpoint
    31  }
    32  
    33  type Registry struct {
    34  	options registry.Options
    35  
    36  	sync.RWMutex
    37  	records  map[string]map[string]*record
    38  	watchers map[string]*Watcher
    39  }
    40  
    41  func NewRegistry(opts ...registry.Option) registry.Registry {
    42  	options := registry.Options{
    43  		Context: context.Background(),
    44  	}
    45  
    46  	for _, o := range opts {
    47  		o(&options)
    48  	}
    49  
    50  	records := getServiceRecords(options.Context)
    51  	if records == nil {
    52  		records = make(map[string]map[string]*record)
    53  	}
    54  
    55  	reg := &Registry{
    56  		options:  options,
    57  		records:  records,
    58  		watchers: make(map[string]*Watcher),
    59  	}
    60  
    61  	go reg.ttlPrune()
    62  
    63  	return reg
    64  }
    65  
    66  func (m *Registry) ttlPrune() {
    67  	prune := time.NewTicker(ttlPruneTime)
    68  	defer prune.Stop()
    69  
    70  	for {
    71  		select {
    72  		case <-prune.C:
    73  			m.Lock()
    74  			for name, records := range m.records {
    75  				for version, record := range records {
    76  					for id, n := range record.Nodes {
    77  						if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
    78  							if logger.V(logger.DebugLevel, logger.DefaultLogger) {
    79  								logger.Debugf("Registry TTL expired for node %s of service %s", n.Id, name)
    80  							}
    81  							delete(m.records[name][version].Nodes, id)
    82  						}
    83  					}
    84  				}
    85  			}
    86  			m.Unlock()
    87  		}
    88  	}
    89  }
    90  
    91  func (m *Registry) sendEvent(r *registry.Result) {
    92  	m.RLock()
    93  	watchers := make([]*Watcher, 0, len(m.watchers))
    94  	for _, w := range m.watchers {
    95  		watchers = append(watchers, w)
    96  	}
    97  	m.RUnlock()
    98  
    99  	for _, w := range watchers {
   100  		select {
   101  		case <-w.exit:
   102  			m.Lock()
   103  			delete(m.watchers, w.id)
   104  			m.Unlock()
   105  		default:
   106  			select {
   107  			case w.res <- r:
   108  			case <-time.After(sendEventTime):
   109  			}
   110  		}
   111  	}
   112  }
   113  
   114  func (m *Registry) Init(opts ...registry.Option) error {
   115  	for _, o := range opts {
   116  		o(&m.options)
   117  	}
   118  
   119  	// add services
   120  	m.Lock()
   121  	defer m.Unlock()
   122  
   123  	records := getServiceRecords(m.options.Context)
   124  	for name, record := range records {
   125  		// add a whole new service including all of its versions
   126  		if _, ok := m.records[name]; !ok {
   127  			m.records[name] = record
   128  			continue
   129  		}
   130  		// add the versions of the service we dont track yet
   131  		for version, r := range record {
   132  			if _, ok := m.records[name][version]; !ok {
   133  				m.records[name][version] = r
   134  				continue
   135  			}
   136  		}
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  func (m *Registry) Options() registry.Options {
   143  	return m.options
   144  }
   145  
   146  func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
   147  	m.Lock()
   148  	defer m.Unlock()
   149  
   150  	var options registry.RegisterOptions
   151  	for _, o := range opts {
   152  		o(&options)
   153  	}
   154  
   155  	r := serviceToRecord(s, options.TTL)
   156  
   157  	if _, ok := m.records[s.Name]; !ok {
   158  		m.records[s.Name] = make(map[string]*record)
   159  	}
   160  
   161  	if _, ok := m.records[s.Name][s.Version]; !ok {
   162  		m.records[s.Name][s.Version] = r
   163  		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   164  			logger.Debugf("Registry added new service: %s, version: %s", s.Name, s.Version)
   165  		}
   166  		go m.sendEvent(&registry.Result{Action: "update", Service: s})
   167  		return nil
   168  	}
   169  
   170  	addedNodes := false
   171  	for _, n := range s.Nodes {
   172  		if _, ok := m.records[s.Name][s.Version].Nodes[n.Id]; !ok {
   173  			addedNodes = true
   174  			metadata := make(map[string]string)
   175  			for k, v := range n.Metadata {
   176  				metadata[k] = v
   177  				m.records[s.Name][s.Version].Nodes[n.Id] = &node{
   178  					Node: &registry.Node{
   179  						Id:       n.Id,
   180  						Address:  n.Address,
   181  						Metadata: metadata,
   182  					},
   183  					TTL:      options.TTL,
   184  					LastSeen: time.Now(),
   185  				}
   186  			}
   187  		}
   188  	}
   189  
   190  	if addedNodes {
   191  		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   192  			logger.Debugf("Registry added new node to service: %s, version: %s", s.Name, s.Version)
   193  		}
   194  		go m.sendEvent(&registry.Result{Action: "update", Service: s})
   195  		return nil
   196  	}
   197  
   198  	// refresh TTL and timestamp
   199  	for _, n := range s.Nodes {
   200  		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   201  			logger.Debugf("Updated registration for service: %s, version: %s", s.Name, s.Version)
   202  		}
   203  		m.records[s.Name][s.Version].Nodes[n.Id].TTL = options.TTL
   204  		m.records[s.Name][s.Version].Nodes[n.Id].LastSeen = time.Now()
   205  	}
   206  
   207  	return nil
   208  }
   209  
   210  func (m *Registry) Deregister(s *registry.Service, opts ...registry.DeregisterOption) error {
   211  	m.Lock()
   212  	defer m.Unlock()
   213  
   214  	if _, ok := m.records[s.Name]; ok {
   215  		if _, ok := m.records[s.Name][s.Version]; ok {
   216  			for _, n := range s.Nodes {
   217  				if _, ok := m.records[s.Name][s.Version].Nodes[n.Id]; ok {
   218  					if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   219  						logger.Debugf("Registry removed node from service: %s, version: %s", s.Name, s.Version)
   220  					}
   221  					delete(m.records[s.Name][s.Version].Nodes, n.Id)
   222  				}
   223  			}
   224  			if len(m.records[s.Name][s.Version].Nodes) == 0 {
   225  				delete(m.records[s.Name], s.Version)
   226  				if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   227  					logger.Debugf("Registry removed service: %s, version: %s", s.Name, s.Version)
   228  				}
   229  			}
   230  		}
   231  		if len(m.records[s.Name]) == 0 {
   232  			delete(m.records, s.Name)
   233  			if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   234  				logger.Debugf("Registry removed service: %s", s.Name)
   235  			}
   236  		}
   237  		go m.sendEvent(&registry.Result{Action: "delete", Service: s})
   238  	}
   239  
   240  	return nil
   241  }
   242  
   243  func (m *Registry) GetService(name string, opts ...registry.GetOption) ([]*registry.Service, error) {
   244  	m.RLock()
   245  	defer m.RUnlock()
   246  
   247  	records, ok := m.records[name]
   248  	if !ok {
   249  		return nil, registry.ErrNotFound
   250  	}
   251  
   252  	services := make([]*registry.Service, len(m.records[name]))
   253  	i := 0
   254  	for _, record := range records {
   255  		services[i] = recordToService(record)
   256  		i++
   257  	}
   258  
   259  	return services, nil
   260  }
   261  
   262  func (m *Registry) ListServices(opts ...registry.ListOption) ([]*registry.Service, error) {
   263  	m.RLock()
   264  	defer m.RUnlock()
   265  
   266  	var services []*registry.Service
   267  	for _, records := range m.records {
   268  		for _, record := range records {
   269  			services = append(services, recordToService(record))
   270  		}
   271  	}
   272  
   273  	return services, nil
   274  }
   275  
   276  func (m *Registry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
   277  	var wo registry.WatchOptions
   278  	for _, o := range opts {
   279  		o(&wo)
   280  	}
   281  
   282  	w := &Watcher{
   283  		exit: make(chan bool),
   284  		res:  make(chan *registry.Result),
   285  		id:   uuid.New().String(),
   286  		wo:   wo,
   287  	}
   288  
   289  	m.Lock()
   290  	m.watchers[w.id] = w
   291  	m.Unlock()
   292  
   293  	return w, nil
   294  }
   295  
   296  func (m *Registry) String() string {
   297  	return "memory"
   298  }