go-micro.dev/v5@v5.12.0/registry/consul/watcher.go (about) 1 package consul 2 3 import ( 4 "sync" 5 6 "github.com/hashicorp/consul/api" 7 "github.com/hashicorp/consul/api/watch" 8 "go-micro.dev/v5/registry" 9 mnet "go-micro.dev/v5/util/net" 10 regutil "go-micro.dev/v5/util/registry" 11 ) 12 13 type consulWatcher struct { 14 r *consulRegistry 15 wo registry.WatchOptions 16 wp *watch.Plan 17 watchers map[string]*watch.Plan 18 19 next chan *registry.Result 20 exit chan bool 21 22 sync.RWMutex 23 services map[string][]*registry.Service 24 } 25 26 func newConsulWatcher(cr *consulRegistry, opts ...registry.WatchOption) (registry.Watcher, error) { 27 var wo registry.WatchOptions 28 for _, o := range opts { 29 o(&wo) 30 } 31 32 cw := &consulWatcher{ 33 r: cr, 34 wo: wo, 35 exit: make(chan bool), 36 next: make(chan *registry.Result, 10), 37 watchers: make(map[string]*watch.Plan), 38 services: make(map[string][]*registry.Service), 39 } 40 41 wp, err := watch.Parse(map[string]interface{}{ 42 "service": wo.Service, 43 "type": "service", 44 }) 45 if err != nil { 46 return nil, err 47 } 48 49 wp.Handler = cw.serviceHandler 50 go wp.RunWithClientAndHclog(cr.Client(), wp.Logger) 51 cw.wp = wp 52 53 return cw, nil 54 } 55 56 func (cw *consulWatcher) serviceHandler(idx uint64, data interface{}) { 57 entries, ok := data.([]*api.ServiceEntry) 58 if !ok { 59 return 60 } 61 62 serviceMap := map[string]*registry.Service{} 63 serviceName := "" 64 65 for _, e := range entries { 66 serviceName = e.Service.Service 67 // version is now a tag 68 version, _ := decodeVersion(e.Service.Tags) 69 // service ID is now the node id 70 id := e.Service.ID 71 // key is always the version 72 key := version 73 // address is service address 74 address := e.Service.Address 75 76 // use node address 77 if len(address) == 0 { 78 address = e.Node.Address 79 } 80 81 svc, ok := serviceMap[key] 82 if !ok { 83 svc = ®istry.Service{ 84 Endpoints: decodeEndpoints(e.Service.Tags), 85 Name: e.Service.Service, 86 Version: version, 87 } 88 serviceMap[key] = svc 89 } 90 91 var del bool 92 93 for _, check := range e.Checks { 94 // delete the node if the status is critical 95 if check.Status == "critical" { 96 del = true 97 break 98 } 99 } 100 101 // if delete then skip the node 102 if del { 103 continue 104 } 105 106 svc.Nodes = append(svc.Nodes, ®istry.Node{ 107 Id: id, 108 Address: mnet.HostPort(address, e.Service.Port), 109 Metadata: decodeMetadata(e.Service.Tags), 110 }) 111 } 112 113 cw.RLock() 114 // make a copy 115 rservices := make(map[string][]*registry.Service) 116 for k, v := range cw.services { 117 rservices[k] = v 118 } 119 cw.RUnlock() 120 121 var newServices []*registry.Service 122 123 // serviceMap is the new set of services keyed by name+version 124 for _, newService := range serviceMap { 125 // append to the new set of cached services 126 newServices = append(newServices, newService) 127 128 // check if the service exists in the existing cache 129 oldServices, ok := rservices[serviceName] 130 if !ok { 131 // does not exist? then we're creating brand new entries 132 cw.next <- ®istry.Result{Action: "create", Service: newService} 133 continue 134 } 135 136 // service exists. ok let's figure out what to update and delete version wise 137 action := "create" 138 139 for _, oldService := range oldServices { 140 // does this version exist? 141 // no? then default to create 142 if oldService.Version != newService.Version { 143 continue 144 } 145 146 // yes? then it's an update 147 action = "update" 148 149 var nodes []*registry.Node 150 // check the old nodes to see if they've been deleted 151 for _, oldNode := range oldService.Nodes { 152 var seen bool 153 for _, newNode := range newService.Nodes { 154 if newNode.Id == oldNode.Id { 155 seen = true 156 break 157 } 158 } 159 // does the old node exist in the new set of nodes 160 // no? then delete that shit 161 if !seen { 162 nodes = append(nodes, oldNode) 163 } 164 } 165 166 // it's an update rather than creation 167 if len(nodes) > 0 { 168 delService := regutil.CopyService(oldService) 169 delService.Nodes = nodes 170 cw.next <- ®istry.Result{Action: "delete", Service: delService} 171 } 172 } 173 174 cw.next <- ®istry.Result{Action: action, Service: newService} 175 } 176 177 // Now check old versions that may not be in new services map 178 for _, old := range rservices[serviceName] { 179 // old version does not exist in new version map 180 // kill it with fire! 181 if _, ok := serviceMap[old.Version]; !ok { 182 cw.next <- ®istry.Result{Action: "delete", Service: old} 183 } 184 } 185 186 // there are no services in the service, empty all services 187 if len(rservices) != 0 && serviceName == "" { 188 for _, services := range rservices { 189 for _, service := range services { 190 cw.next <- ®istry.Result{Action: "delete", Service: service} 191 } 192 } 193 } 194 195 cw.Lock() 196 cw.services[serviceName] = newServices 197 cw.Unlock() 198 } 199 200 func (cw *consulWatcher) handle(idx uint64, data interface{}) { 201 services, ok := data.(map[string][]string) 202 if !ok { 203 return 204 } 205 206 // add new watchers 207 for service := range services { 208 // Filter on watch options 209 // wo.Service: Only watch services we care about 210 if len(cw.wo.Service) > 0 && service != cw.wo.Service { 211 continue 212 } 213 214 if _, ok := cw.watchers[service]; ok { 215 continue 216 } 217 wp, err := watch.Parse(map[string]interface{}{ 218 "type": "service", 219 "service": service, 220 }) 221 if err == nil { 222 wp.Handler = cw.serviceHandler 223 go wp.RunWithClientAndHclog(cw.r.Client(), wp.Logger) 224 cw.watchers[service] = wp 225 cw.next <- ®istry.Result{Action: "create", Service: ®istry.Service{Name: service}} 226 } 227 } 228 229 cw.RLock() 230 // make a copy 231 rservices := make(map[string][]*registry.Service) 232 for k, v := range cw.services { 233 rservices[k] = v 234 } 235 cw.RUnlock() 236 237 // remove unknown services from registry 238 // save the things we want to delete 239 deleted := make(map[string][]*registry.Service) 240 241 for service := range rservices { 242 if _, ok := services[service]; !ok { 243 cw.Lock() 244 // save this before deleting 245 deleted[service] = cw.services[service] 246 delete(cw.services, service) 247 cw.Unlock() 248 } 249 } 250 251 // remove unknown services from watchers 252 for service, w := range cw.watchers { 253 if _, ok := services[service]; !ok { 254 w.Stop() 255 delete(cw.watchers, service) 256 for _, oldService := range deleted[service] { 257 // send a delete for the service nodes that we're removing 258 cw.next <- ®istry.Result{Action: "delete", Service: oldService} 259 } 260 // sent the empty list as the last resort to indicate to delete the entire service 261 cw.next <- ®istry.Result{Action: "delete", Service: ®istry.Service{Name: service}} 262 } 263 } 264 } 265 266 func (cw *consulWatcher) Next() (*registry.Result, error) { 267 select { 268 case <-cw.exit: 269 return nil, registry.ErrWatcherStopped 270 case r, ok := <-cw.next: 271 if !ok { 272 return nil, registry.ErrWatcherStopped 273 } 274 return r, nil 275 } 276 } 277 278 func (cw *consulWatcher) Stop() { 279 select { 280 case <-cw.exit: 281 return 282 default: 283 close(cw.exit) 284 if cw.wp == nil { 285 return 286 } 287 cw.wp.Stop() 288 289 // drain results 290 for { 291 select { 292 case <-cw.next: 293 default: 294 return 295 } 296 } 297 } 298 }