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 = ®istry.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(®istry.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(®istry.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(®istry.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] = ®istry.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 }