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 }