go-micro.dev/v5@v5.12.0/registry/nats/nats.go (about)

     1  // Package nats provides a NATS registry using broadcast queries
     2  package nats
     3  
     4  import (
     5  	"context"
     6  	"encoding/json"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/nats-io/nats.go"
    12  	"go-micro.dev/v5/registry"
    13  )
    14  
    15  type natsRegistry struct {
    16  	addrs          []string
    17  	opts           registry.Options
    18  	nopts          nats.Options
    19  	queryTopic     string
    20  	watchTopic     string
    21  	registerAction string
    22  
    23  	sync.RWMutex
    24  	conn      *nats.Conn
    25  	services  map[string][]*registry.Service
    26  	listeners map[string]chan bool
    27  }
    28  
    29  var (
    30  	defaultQueryTopic     = "micro.nats.query"
    31  	defaultWatchTopic     = "micro.nats.watch"
    32  	defaultRegisterAction = "create"
    33  )
    34  
    35  func configure(n *natsRegistry, opts ...registry.Option) error {
    36  	for _, o := range opts {
    37  		o(&n.opts)
    38  	}
    39  
    40  	natsOptions := nats.GetDefaultOptions()
    41  	if n, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok {
    42  		natsOptions = n
    43  	}
    44  
    45  	queryTopic := defaultQueryTopic
    46  	if qt, ok := n.opts.Context.Value(queryTopicKey{}).(string); ok {
    47  		queryTopic = qt
    48  	}
    49  
    50  	watchTopic := defaultWatchTopic
    51  	if wt, ok := n.opts.Context.Value(watchTopicKey{}).(string); ok {
    52  		watchTopic = wt
    53  	}
    54  
    55  	registerAction := defaultRegisterAction
    56  	if ra, ok := n.opts.Context.Value(registerActionKey{}).(string); ok {
    57  		registerAction = ra
    58  	}
    59  
    60  	// Options have higher priority than nats.Options
    61  	// only if Addrs, Secure or TLSConfig were not set through a Option
    62  	// we read them from nats.Option
    63  	if len(n.opts.Addrs) == 0 {
    64  		n.opts.Addrs = natsOptions.Servers
    65  	}
    66  
    67  	if !n.opts.Secure {
    68  		n.opts.Secure = natsOptions.Secure
    69  	}
    70  
    71  	if n.opts.TLSConfig == nil {
    72  		n.opts.TLSConfig = natsOptions.TLSConfig
    73  	}
    74  
    75  	// check & add nats:// prefix (this makes also sure that the addresses
    76  	// stored in natsaddrs and n.opts.Addrs are identical)
    77  	n.opts.Addrs = setAddrs(n.opts.Addrs)
    78  
    79  	n.addrs = n.opts.Addrs
    80  	n.nopts = natsOptions
    81  	n.queryTopic = queryTopic
    82  	n.watchTopic = watchTopic
    83  	n.registerAction = registerAction
    84  
    85  	return nil
    86  }
    87  
    88  func setAddrs(addrs []string) []string {
    89  	var cAddrs []string
    90  	for _, addr := range addrs {
    91  		if len(addr) == 0 {
    92  			continue
    93  		}
    94  		if !strings.HasPrefix(addr, "nats://") {
    95  			addr = "nats://" + addr
    96  		}
    97  		cAddrs = append(cAddrs, addr)
    98  	}
    99  	if len(cAddrs) == 0 {
   100  		cAddrs = []string{nats.DefaultURL}
   101  	}
   102  	return cAddrs
   103  }
   104  
   105  func (n *natsRegistry) newConn() (*nats.Conn, error) {
   106  	opts := n.nopts
   107  	opts.Servers = n.addrs
   108  	opts.Secure = n.opts.Secure
   109  	opts.TLSConfig = n.opts.TLSConfig
   110  
   111  	// secure might not be set
   112  	if opts.TLSConfig != nil {
   113  		opts.Secure = true
   114  	}
   115  
   116  	return opts.Connect()
   117  }
   118  
   119  func (n *natsRegistry) getConn() (*nats.Conn, error) {
   120  	n.Lock()
   121  	defer n.Unlock()
   122  
   123  	if n.conn != nil {
   124  		return n.conn, nil
   125  	}
   126  
   127  	c, err := n.newConn()
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	n.conn = c
   132  
   133  	return n.conn, nil
   134  }
   135  
   136  func (n *natsRegistry) register(s *registry.Service) error {
   137  	conn, err := n.getConn()
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	n.Lock()
   143  	defer n.Unlock()
   144  
   145  	// cache service
   146  	n.services[s.Name] = addServices(n.services[s.Name], cp([]*registry.Service{s}))
   147  
   148  	// create query listener
   149  	if n.listeners[s.Name] == nil {
   150  		listener := make(chan bool)
   151  
   152  		// create a subscriber that responds to queries
   153  		sub, err := conn.Subscribe(n.queryTopic, func(m *nats.Msg) {
   154  			var result *registry.Result
   155  
   156  			if err := json.Unmarshal(m.Data, &result); err != nil {
   157  				return
   158  			}
   159  
   160  			var services []*registry.Service
   161  
   162  			switch result.Action {
   163  			// is this a get query and we own the service?
   164  			case "get":
   165  				if result.Service.Name != s.Name {
   166  					return
   167  				}
   168  				n.RLock()
   169  				services = cp(n.services[s.Name])
   170  				n.RUnlock()
   171  			// it's a list request, but we're still only a
   172  			// subscriber for this service... so just get this service
   173  			// totally suboptimal
   174  			case "list":
   175  				n.RLock()
   176  				services = cp(n.services[s.Name])
   177  				n.RUnlock()
   178  			default:
   179  				// does not match
   180  				return
   181  			}
   182  
   183  			// respond to query
   184  			for _, service := range services {
   185  				b, err := json.Marshal(service)
   186  				if err != nil {
   187  					continue
   188  				}
   189  				conn.Publish(m.Reply, b)
   190  			}
   191  		})
   192  		if err != nil {
   193  			return err
   194  		}
   195  
   196  		// Unsubscribe if we're told to do so
   197  		go func() {
   198  			<-listener
   199  			sub.Unsubscribe()
   200  		}()
   201  
   202  		n.listeners[s.Name] = listener
   203  	}
   204  
   205  	return nil
   206  }
   207  
   208  func (n *natsRegistry) deregister(s *registry.Service) error {
   209  	n.Lock()
   210  	defer n.Unlock()
   211  
   212  	services := delServices(n.services[s.Name], cp([]*registry.Service{s}))
   213  	if len(services) > 0 {
   214  		n.services[s.Name] = services
   215  		return nil
   216  	}
   217  
   218  	// delete cached service
   219  	delete(n.services, s.Name)
   220  
   221  	// delete query listener
   222  	if listener, lexists := n.listeners[s.Name]; lexists {
   223  		close(listener)
   224  		delete(n.listeners, s.Name)
   225  	}
   226  
   227  	return nil
   228  }
   229  
   230  func (n *natsRegistry) query(s string, quorum int) ([]*registry.Service, error) {
   231  	conn, err := n.getConn()
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	var action string
   237  	var service *registry.Service
   238  
   239  	if len(s) > 0 {
   240  		action = "get"
   241  		service = &registry.Service{Name: s}
   242  	} else {
   243  		action = "list"
   244  	}
   245  
   246  	inbox := nats.NewInbox()
   247  
   248  	response := make(chan *registry.Service, 10)
   249  
   250  	sub, err := conn.Subscribe(inbox, func(m *nats.Msg) {
   251  		var service *registry.Service
   252  		if err := json.Unmarshal(m.Data, &service); err != nil {
   253  			return
   254  		}
   255  		select {
   256  		case response <- service:
   257  		case <-time.After(n.opts.Timeout):
   258  		}
   259  	})
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	defer sub.Unsubscribe()
   264  
   265  	b, err := json.Marshal(&registry.Result{Action: action, Service: service})
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	if err := conn.PublishMsg(&nats.Msg{
   271  		Subject: n.queryTopic,
   272  		Reply:   inbox,
   273  		Data:    b,
   274  	}); err != nil {
   275  		return nil, err
   276  	}
   277  
   278  	timeoutChan := time.After(n.opts.Timeout)
   279  
   280  	serviceMap := make(map[string]*registry.Service)
   281  
   282  loop:
   283  	for {
   284  		select {
   285  		case service := <-response:
   286  			key := service.Name + "-" + service.Version
   287  			srv, ok := serviceMap[key]
   288  			if ok {
   289  				srv.Nodes = append(srv.Nodes, service.Nodes...)
   290  				serviceMap[key] = srv
   291  			} else {
   292  				serviceMap[key] = service
   293  			}
   294  
   295  			if quorum > 0 && len(serviceMap[key].Nodes) >= quorum {
   296  				break loop
   297  			}
   298  		case <-timeoutChan:
   299  			break loop
   300  		}
   301  	}
   302  
   303  	var services []*registry.Service
   304  	for _, service := range serviceMap {
   305  		services = append(services, service)
   306  	}
   307  	return services, nil
   308  }
   309  
   310  func (n *natsRegistry) Init(opts ...registry.Option) error {
   311  	return configure(n, opts...)
   312  }
   313  
   314  func (n *natsRegistry) Options() registry.Options {
   315  	return n.opts
   316  }
   317  
   318  func (n *natsRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
   319  	if err := n.register(s); err != nil {
   320  		return err
   321  	}
   322  
   323  	conn, err := n.getConn()
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	b, err := json.Marshal(&registry.Result{Action: n.registerAction, Service: s})
   329  	if err != nil {
   330  		return err
   331  	}
   332  
   333  	return conn.Publish(n.watchTopic, b)
   334  }
   335  
   336  func (n *natsRegistry) Deregister(s *registry.Service, opts ...registry.DeregisterOption) error {
   337  	if err := n.deregister(s); err != nil {
   338  		return err
   339  	}
   340  
   341  	conn, err := n.getConn()
   342  	if err != nil {
   343  		return err
   344  	}
   345  
   346  	b, err := json.Marshal(&registry.Result{Action: "delete", Service: s})
   347  	if err != nil {
   348  		return err
   349  	}
   350  	return conn.Publish(n.watchTopic, b)
   351  }
   352  
   353  func (n *natsRegistry) GetService(s string, opts ...registry.GetOption) ([]*registry.Service, error) {
   354  	services, err := n.query(s, getQuorum(n.opts))
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  	return services, nil
   359  }
   360  
   361  func (n *natsRegistry) ListServices(opts ...registry.ListOption) ([]*registry.Service, error) {
   362  	s, err := n.query("", 0)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  
   367  	var services []*registry.Service
   368  	serviceMap := make(map[string]*registry.Service)
   369  
   370  	for _, v := range s {
   371  		serviceMap[v.Name] = &registry.Service{Name: v.Name, Version: v.Version}
   372  	}
   373  
   374  	for _, v := range serviceMap {
   375  		services = append(services, v)
   376  	}
   377  
   378  	return services, nil
   379  }
   380  
   381  func (n *natsRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
   382  	conn, err := n.getConn()
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  
   387  	sub, err := conn.SubscribeSync(n.watchTopic)
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  
   392  	var wo registry.WatchOptions
   393  	for _, o := range opts {
   394  		o(&wo)
   395  	}
   396  
   397  	return &natsWatcher{sub, wo}, nil
   398  }
   399  
   400  func (n *natsRegistry) String() string {
   401  	return "nats"
   402  }
   403  
   404  func NewNatsRegistry(opts ...registry.Option) registry.Registry {
   405  	options := registry.Options{
   406  		Timeout: time.Millisecond * 100,
   407  		Context: context.Background(),
   408  	}
   409  
   410  	n := &natsRegistry{
   411  		opts:      options,
   412  		services:  make(map[string][]*registry.Service),
   413  		listeners: make(map[string]chan bool),
   414  	}
   415  	configure(n, opts...)
   416  	return n
   417  }