gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/registry/mdns_registry.go (about)

     1  // Package mdns is a multicast dns registry
     2  package registry
     3  
     4  import (
     5  	"context"
     6  	"fmt"
     7  	"net"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/google/uuid"
    14  	"github.com/micro/mdns"
    15  )
    16  
    17  var (
    18  	// use a .micro domain rather than .local
    19  	mdnsDomain = "micro"
    20  )
    21  
    22  type mdnsTxt struct {
    23  	Service   string
    24  	Version   string
    25  	Endpoints []*Endpoint
    26  	Metadata  map[string]string
    27  }
    28  
    29  type mdnsEntry struct {
    30  	id   string
    31  	node *mdns.Server
    32  }
    33  
    34  type mdnsRegistry struct {
    35  	opts Options
    36  	// the mdns domain
    37  	domain string
    38  
    39  	sync.Mutex
    40  	services map[string][]*mdnsEntry
    41  
    42  	mtx sync.RWMutex
    43  
    44  	// watchers
    45  	watchers map[string]*mdnsWatcher
    46  
    47  	// listener
    48  	listener chan *mdns.ServiceEntry
    49  }
    50  
    51  func newRegistry(opts ...Option) Registry {
    52  	options := Options{
    53  		Context: context.Background(),
    54  		Timeout: time.Millisecond * 100,
    55  	}
    56  
    57  	for _, o := range opts {
    58  		o(&options)
    59  	}
    60  
    61  	// set the domain
    62  	domain := mdnsDomain
    63  
    64  	d, ok := options.Context.Value("mdns.domain").(string)
    65  	if ok {
    66  		domain = d
    67  	}
    68  
    69  	return &mdnsRegistry{
    70  		opts:     options,
    71  		domain:   domain,
    72  		services: make(map[string][]*mdnsEntry),
    73  		watchers: make(map[string]*mdnsWatcher),
    74  	}
    75  }
    76  
    77  func (m *mdnsRegistry) Init(opts ...Option) error {
    78  	for _, o := range opts {
    79  		o(&m.opts)
    80  	}
    81  	return nil
    82  }
    83  
    84  func (m *mdnsRegistry) Options() Options {
    85  	return m.opts
    86  }
    87  
    88  func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error {
    89  	m.Lock()
    90  	defer m.Unlock()
    91  
    92  	entries, ok := m.services[service.Name]
    93  	// first entry, create wildcard used for list queries
    94  	if !ok {
    95  		s, err := mdns.NewMDNSService(
    96  			service.Name,
    97  			"_services",
    98  			m.domain+".",
    99  			"",
   100  			9999,
   101  			[]net.IP{net.ParseIP("0.0.0.0")},
   102  			nil,
   103  		)
   104  		if err != nil {
   105  			return err
   106  		}
   107  
   108  		srv, err := mdns.NewServer(&mdns.Config{Zone: &mdns.DNSSDService{MDNSService: s}})
   109  		if err != nil {
   110  			return err
   111  		}
   112  
   113  		// append the wildcard entry
   114  		entries = append(entries, &mdnsEntry{id: "*", node: srv})
   115  	}
   116  
   117  	var gerr error
   118  
   119  	for _, node := range service.Nodes {
   120  		var seen bool
   121  		var e *mdnsEntry
   122  
   123  		for _, entry := range entries {
   124  			if node.Id == entry.id {
   125  				seen = true
   126  				e = entry
   127  				break
   128  			}
   129  		}
   130  
   131  		// already registered, continue
   132  		if seen {
   133  			continue
   134  			// doesn't exist
   135  		} else {
   136  			e = &mdnsEntry{}
   137  		}
   138  
   139  		txt, err := encode(&mdnsTxt{
   140  			Service:   service.Name,
   141  			Version:   service.Version,
   142  			Endpoints: service.Endpoints,
   143  			Metadata:  node.Metadata,
   144  		})
   145  
   146  		if err != nil {
   147  			gerr = err
   148  			continue
   149  		}
   150  
   151  		//
   152  		host, pt, err := net.SplitHostPort(node.Address)
   153  		if err != nil {
   154  			gerr = err
   155  			continue
   156  		}
   157  		port, _ := strconv.Atoi(pt)
   158  
   159  		// we got here, new node
   160  		s, err := mdns.NewMDNSService(
   161  			node.Id,
   162  			service.Name,
   163  			m.domain+".",
   164  			"",
   165  			port,
   166  			[]net.IP{net.ParseIP(host)},
   167  			txt,
   168  		)
   169  		if err != nil {
   170  			gerr = err
   171  			continue
   172  		}
   173  
   174  		srv, err := mdns.NewServer(&mdns.Config{Zone: s})
   175  		if err != nil {
   176  			gerr = err
   177  			continue
   178  		}
   179  
   180  		e.id = node.Id
   181  		e.node = srv
   182  		entries = append(entries, e)
   183  	}
   184  
   185  	// save
   186  	m.services[service.Name] = entries
   187  
   188  	return gerr
   189  }
   190  
   191  func (m *mdnsRegistry) Deregister(service *Service) error {
   192  	m.Lock()
   193  	defer m.Unlock()
   194  
   195  	var newEntries []*mdnsEntry
   196  
   197  	// loop existing entries, check if any match, shutdown those that do
   198  	for _, entry := range m.services[service.Name] {
   199  		var remove bool
   200  
   201  		for _, node := range service.Nodes {
   202  			if node.Id == entry.id {
   203  				entry.node.Shutdown()
   204  				remove = true
   205  				break
   206  			}
   207  		}
   208  
   209  		// keep it?
   210  		if !remove {
   211  			newEntries = append(newEntries, entry)
   212  		}
   213  	}
   214  
   215  	// last entry is the wildcard for list queries. Remove it.
   216  	if len(newEntries) == 1 && newEntries[0].id == "*" {
   217  		newEntries[0].node.Shutdown()
   218  		delete(m.services, service.Name)
   219  	} else {
   220  		m.services[service.Name] = newEntries
   221  	}
   222  
   223  	return nil
   224  }
   225  
   226  func (m *mdnsRegistry) GetService(service string) ([]*Service, error) {
   227  	serviceMap := make(map[string]*Service)
   228  	entries := make(chan *mdns.ServiceEntry, 10)
   229  	done := make(chan bool)
   230  
   231  	p := mdns.DefaultParams(service)
   232  	// set context with timeout
   233  	var cancel context.CancelFunc
   234  	p.Context, cancel = context.WithTimeout(context.Background(), m.opts.Timeout)
   235  	defer cancel()
   236  	// set entries channel
   237  	p.Entries = entries
   238  	// set the domain
   239  	p.Domain = m.domain
   240  
   241  	go func() {
   242  		for {
   243  			select {
   244  			case e := <-entries:
   245  				// list record so skip
   246  				if p.Service == "_services" {
   247  					continue
   248  				}
   249  				if p.Domain != m.domain {
   250  					continue
   251  				}
   252  				if e.TTL == 0 {
   253  					continue
   254  				}
   255  
   256  				txt, err := decode(e.InfoFields)
   257  				if err != nil {
   258  					continue
   259  				}
   260  
   261  				if txt.Service != service {
   262  					continue
   263  				}
   264  
   265  				s, ok := serviceMap[txt.Version]
   266  				if !ok {
   267  					s = &Service{
   268  						Name:      txt.Service,
   269  						Version:   txt.Version,
   270  						Endpoints: txt.Endpoints,
   271  					}
   272  				}
   273  
   274  				s.Nodes = append(s.Nodes, &Node{
   275  					Id:       strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+"."),
   276  					Address:  fmt.Sprintf("%s:%d", e.AddrV4.String(), e.Port),
   277  					Metadata: txt.Metadata,
   278  				})
   279  
   280  				serviceMap[txt.Version] = s
   281  			case <-p.Context.Done():
   282  				close(done)
   283  				return
   284  			}
   285  		}
   286  	}()
   287  
   288  	// execute the query
   289  	if err := mdns.Query(p); err != nil {
   290  		return nil, err
   291  	}
   292  
   293  	// wait for completion
   294  	<-done
   295  
   296  	// create list and return
   297  	services := make([]*Service, 0, len(serviceMap))
   298  
   299  	for _, service := range serviceMap {
   300  		services = append(services, service)
   301  	}
   302  
   303  	return services, nil
   304  }
   305  
   306  func (m *mdnsRegistry) ListServices() ([]*Service, error) {
   307  	serviceMap := make(map[string]bool)
   308  	entries := make(chan *mdns.ServiceEntry, 10)
   309  	done := make(chan bool)
   310  
   311  	p := mdns.DefaultParams("_services")
   312  	// set context with timeout
   313  	var cancel context.CancelFunc
   314  	p.Context, cancel = context.WithTimeout(context.Background(), m.opts.Timeout)
   315  	defer cancel()
   316  	// set entries channel
   317  	p.Entries = entries
   318  	// set domain
   319  	p.Domain = m.domain
   320  
   321  	var services []*Service
   322  
   323  	go func() {
   324  		for {
   325  			select {
   326  			case e := <-entries:
   327  				if e.TTL == 0 {
   328  					continue
   329  				}
   330  				if !strings.HasSuffix(e.Name, p.Domain+".") {
   331  					continue
   332  				}
   333  				name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".")
   334  				if !serviceMap[name] {
   335  					serviceMap[name] = true
   336  					services = append(services, &Service{Name: name})
   337  				}
   338  			case <-p.Context.Done():
   339  				close(done)
   340  				return
   341  			}
   342  		}
   343  	}()
   344  
   345  	// execute query
   346  	if err := mdns.Query(p); err != nil {
   347  		return nil, err
   348  	}
   349  
   350  	// wait till done
   351  	<-done
   352  
   353  	return services, nil
   354  }
   355  
   356  func (m *mdnsRegistry) Watch(opts ...WatchOption) (Watcher, error) {
   357  	var wo WatchOptions
   358  	for _, o := range opts {
   359  		o(&wo)
   360  	}
   361  
   362  	md := &mdnsWatcher{
   363  		id:       uuid.New().String(),
   364  		wo:       wo,
   365  		ch:       make(chan *mdns.ServiceEntry, 32),
   366  		exit:     make(chan struct{}),
   367  		domain:   m.domain,
   368  		registry: m,
   369  	}
   370  
   371  	m.mtx.Lock()
   372  	defer m.mtx.Unlock()
   373  
   374  	// save the watcher
   375  	m.watchers[md.id] = md
   376  
   377  	// check of the listener exists
   378  	if m.listener != nil {
   379  		return md, nil
   380  	}
   381  
   382  	// start the listener
   383  	go func() {
   384  		// go to infinity
   385  		for {
   386  			m.mtx.Lock()
   387  
   388  			// just return if there are no watchers
   389  			if len(m.watchers) == 0 {
   390  				m.listener = nil
   391  				m.mtx.Unlock()
   392  				return
   393  			}
   394  
   395  			// check existing listener
   396  			if m.listener != nil {
   397  				m.mtx.Unlock()
   398  				return
   399  			}
   400  
   401  			// reset the listener
   402  			exit := make(chan struct{})
   403  			ch := make(chan *mdns.ServiceEntry, 32)
   404  			m.listener = ch
   405  
   406  			m.mtx.Unlock()
   407  
   408  			// send messages to the watchers
   409  			go func() {
   410  				send := func(w *mdnsWatcher, e *mdns.ServiceEntry) {
   411  					select {
   412  					case w.ch <- e:
   413  					default:
   414  					}
   415  				}
   416  
   417  				for {
   418  					select {
   419  					case <-exit:
   420  						return
   421  					case e, ok := <-ch:
   422  						if !ok {
   423  							return
   424  						}
   425  						m.mtx.RLock()
   426  						// send service entry to all watchers
   427  						for _, w := range m.watchers {
   428  							send(w, e)
   429  						}
   430  						m.mtx.RUnlock()
   431  					}
   432  				}
   433  
   434  			}()
   435  
   436  			// start listening, blocking call
   437  			mdns.Listen(ch, exit)
   438  
   439  			// mdns.Listen has unblocked
   440  			// kill the saved listener
   441  			m.mtx.Lock()
   442  			m.listener = nil
   443  			close(ch)
   444  			m.mtx.Unlock()
   445  		}
   446  	}()
   447  
   448  	return md, nil
   449  }
   450  
   451  func (m *mdnsRegistry) String() string {
   452  	return "mdns"
   453  }
   454  
   455  // NewRegistry returns a new default registry which is mdns
   456  func NewRegistry(opts ...Option) Registry {
   457  	return newRegistry(opts...)
   458  }